[no description]
init
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.toml | 10 | ||||
| -rw-r--r-- | LICENSE | 21 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | build.rs | 97 | ||||
| -rw-r--r-- | src/implementations.rs | 150 | ||||
| -rw-r--r-- | src/lib.rs | 170 | ||||
| -rw-r--r-- | src/picks.rs | 33 |
8 files changed, 485 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03314f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..503a575 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ttools" +version = "0.1.0" +license = "MIT" +edition = "2024" +authors = ["bend-n <[email protected]>"] +description = "tuple utilities" +repository = "https://git.bendn.org/ttools" +keywords = ["tuple", "utilities"] +categories = ["algorithms", "no-std"] @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 bendn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a67b54b --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# `ttools` + +for when you really, really, wanna pretend your tuple is a heterogenous array. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..1fabe52 --- /dev/null +++ b/build.rs @@ -0,0 +1,97 @@ +use std::{fs::File, io::Write, path::Path}; + +fn main() { + let e = std::env::var("OUT_DIR").unwrap(); + let mut impl_td = File::create(Path::new(&e).join("impl_td.rs")).unwrap(); + let mut impl_rv = File::create(Path::new(&e).join("impl_rv.rs")).unwrap(); + let mut impl_pk = File::create(Path::new(&e).join("impl_pk.rs")).unwrap(); + for n in 0..26 { + let z = ('A'..='Z').take(n).collect::<Vec<_>>().into_iter(); + generate(z, &mut impl_td, &mut impl_rv, &mut impl_pk); + } +} +fn tup(x: impl Iterator<Item = char>) -> String { + let mut s = String::default(); + for lem in x { + s.push(lem); + s.push(','); + } + + s +} +fn generate( + x: impl Iterator<Item = char> + Clone + DoubleEndedIterator + ExactSizeIterator, + impl_td: &mut impl Write, + impl_rv: &mut impl Write, + impl_pk: &mut impl Write, +) { + let tupl = tup(x.clone()); + let n = x.clone().count(); + for i in 0..n.min(10) { + let drop = tup(x.clone().skip(i)); + let take = tup(x.clone().take(i)); + let take_b = tup(x.clone().rev().take(i).rev()); + let drop_b = tup(x.clone().rev().skip(i).rev()); + write!( + impl_td, + " + impl<{tupl}> TD<{i}> for ({tupl}) {{ + type Drop = ({drop}); + type Take = ({take}); + type TakeB = ({take_b}); + type DropB = ({drop_b}); + + fn dropf(self) -> (Self::Take, Self::Drop) {{ + let ({tupl}) = self; + (({take}), ({drop})) + }} + + fn dropb(self) -> (Self::DropB, Self::TakeB) {{ + let ({tupl}) = self; + (({drop_b}), ({take_b})) + }} + }} + ", + ) + .unwrap(); + + write!( + impl_pk, + " + impl<{tupl}> Pick<{i}> for ({tupl}) {{ + type At = {at}; + type L = ({left}); + type R = ({right}); + fn repick(({left}): Self::L, at: Self::At, ({right}): Self::R) -> Self {{ + ({left} at, {right}) + }} + fn depict(self) -> (Self::L, Self::At, Self::R) {{ + let ({tupl}) = self; + (({left}), {at}, ({right})) + }} + }} + ", + at = x.clone().nth(i).unwrap(), + left = tup(x.clone().take(i)), + right = tup(x.clone().skip(i + 1)) + ) + .unwrap(); + } + let rev = tup(x.clone().rev()); + write!( + impl_rv, + " + #[allow(non_snake_case)] + impl<{tupl}> Reverse for ({tupl}) {{ + type Reversed = ({rev}); + + fn reverse(self) -> Self::Reversed {{ + let ({x}) = self; + ({rev}) + }} + }} + ", + x = tup(x.clone()), + ) + .unwrap(); +} diff --git a/src/implementations.rs b/src/implementations.rs new file mode 100644 index 0000000..2f2eb54 --- /dev/null +++ b/src/implementations.rs @@ -0,0 +1,150 @@ +#![allow(non_snake_case)] +use super::*; +include!(concat!(env!("OUT_DIR"), "/impl_td.rs")); +include!(concat!(env!("OUT_DIR"), "/impl_rv.rs")); +include!(concat!(env!("OUT_DIR"), "/impl_pk.rs")); + +macro_rules! generate {( + $($Hd:tt $($T:tt)*)? +) => ( + $(generate! { $($T)* })? + do_impl! { [$] $($($T)* [$] $Hd)? } +)} +// impl Reverse for () { +// type Reversed = (); + +// fn reverse(self) -> Self::Reversed { +// () +// } +// } +impl Tupl for () { + const LEN: usize = 0; + type Head = !; + type Last = !; + type Tail = (); + type Init = (); + type Inner = (); + + fn uncons(self) -> (Self::Head, Self::Tail) { + todo!() + } + fn unsnoc(self) -> (Self::Init, Self::Last) { + todo!() + } + fn fcons(head: Self::Head, _: Self::Inner, _: Self::Last) -> Self { + match head {} + } + fn cons(head: Self::Head, _: Self::Tail) -> Self { + match head {} + } + fn snoc(_: Self::Init, last: Self::Last) -> Self { + match last {} + } +} +impl<T> Tupl for (T,) { + const LEN: usize = 1; + type Head = T; + type Last = T; + type Tail = (); + type Init = (); + type Inner = (); + + fn uncons(self) -> (Self::Head, Self::Tail) { + unimplemented!("dont call base case.") + } + + fn unsnoc(self) -> (Self::Init, Self::Last) { + unimplemented!("dont call base case.") + } + + fn cons(_: Self::Head, _: Self::Tail) -> Self { + unimplemented!("dont call base case.") + } + fn snoc(_: Self::Init, _: Self::Last) -> Self { + unimplemented!("dont call base case.") + } + fn fcons(_: Self::Head, _: Self::Inner, _: Self::Last) -> Self { + unimplemented!("dont call base case.") + } +} + +macro_rules! do_impl { +([$_:tt]) => {}; +([$_:tt] [$] $i:tt) => {}; +// ([$_:tt] $i:ident [$] $x:tt) => {}; +( + [$_:tt] // `$` sigil + $hd:tt $($i:ident)* [$] $tl:tt +) => ( + // impl<T> Seal for ($($i, )*) {} + impl<$($i,)* Α> Cons<Α> for ($($i,)*) { + type Cons = (Α, $($i,)*); + type Snoc = ($($i,)* Α,); + } + + #[allow(non_snake_case)] + impl<$hd, $($i,)* $tl> Tupl for ($hd, $($i,)* $tl) { + const LEN: usize = 0 $(+ { stringify!($i); 1 } )*; + type Head = $hd; + type Last = $tl; + type Tail = ($($i,)* $tl,); + type Init = ($hd, $($i,)*); + type Inner = ($($i,)*); + fn cons(head: Self::Head, ($($i,)* $tl,): Self::Tail) -> Self { + (head, $($i,)* $tl,) + } + fn snoc(($hd, $($i,)*): Self::Init, last: Self::Last) -> Self { + ($hd, $($i,)* last) + } + fn fcons(head: Self::Head, ($($i,)*): Self::Inner, last: Self::Last) -> Self { + (head, $($i,)* last) + } + fn uncons(self) -> (Self::Head, Self::Tail) { + let ($hd, $($i,)* $tl) = self; + ($hd, ($($i,)* $tl,)) + } + fn unsnoc(self) -> (Self::Init, Self::Last) { + let ($hd, $($i,)* $tl) = self; + (($hd, $($i,)*), $tl) + } + // fn array(self) -> [T; 0 $(+ { stringify!($i); 1 } )*] { + // with_vars! { + // [acc: ] + // [to_munch: $($i)*] + // |$_ x| { + // let ($_($_ x, )*) = self; + // [$_($_ x, )*] + // } + // } + // } + // fn tuple(x: [T; 0 $(+ { stringify!($i); 1 } )*]) -> ($($i, )*) { + // with_vars! { + // [acc: ] + // [to_munch: $($i)*] + // |$_ x| { + // let [$_($x, )*] = x; + // ($_($x, )*) + // } + // } + // } + } +); +} + +generate!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z); + +impl<T, U: Cons<T>> Push<T> for U { + type Result = U::Snoc; + + fn push(self, other: T) -> Self::Result { + crate::snoc(self, other) + } +} + +impl<T, U: Cons<T>> With<U> for T { + type Result = U::Cons; + + fn with(self, other: U) -> Self::Result { + crate::cons(self, other) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2b2f999 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,170 @@ +#![feature(tuple_trait, import_trait_associated_functions, never_type)] +#![no_std] +#![allow(mixed_script_confusables, confusable_idents)] +//!  +//! +//! start with [`Tupl`]. +mod implementations; +mod picks; +/// Reverse a tuple. +pub trait Reverse: Tupl { + type Reversed: Tupl; + fn reverse(self) -> Self::Reversed; +} +#[doc(inline)] +pub use Reverse::{reverse, reverse as flip}; +#[doc(inline)] +pub use Tupl::{drop, head, head as left, head as fst, last, last as right, pick, take}; +pub use picks::*; +/// Construct a tuple. +pub fn cons<T, Tup: Cons<T>>(x: T, y: Tup) -> Tup::Cons { + Tup::Cons::cons(x, y) +} +/// Construct a tuple, backwards. +pub fn snoc<T, Tup: Cons<T>>(x: Tup, y: T) -> Tup::Snoc { + Tup::Snoc::snoc(x, y) +} +/// alias for [`snoc`] +pub trait Push<T>: Tupl { + type Result; + /// ``` + /// use ttools::*; + /// let x = (1, 2).push(3); + /// assert_eq!(x, ((1, 2, 3))); + /// ``` + fn push(self, other: T) -> Self::Result; +} +/// alias for [`cons`] +pub trait With<T: Tupl> { + type Result; + /// ``` + /// use ttools::*; + /// let x = 1.with((2, 3)); + /// assert_eq!(x, ((1, 2, 3))); + /// ``` + fn with(self, other: T) -> Self::Result; +} + +// implemented for the constructed type +pub trait Cons<T>: Tupl { + /// (T, ..self) + type Cons: Tupl<Head = T, Tail = Self>; + /// (..self, T) + type Snoc: Tupl<Last = T, Init = Self>; +} +/// backing trait for [`take`]. +pub trait TD<const N: usize>: Tupl { + /// self[N..] + type Drop: Tupl; + /// self[..N] + type Take: Tupl; + // self[LEN - N..] + type TakeB: Tupl; + /// self[..LEN - N] + type DropB: Tupl; + /// deconstruct + fn dropf(self) -> (Self::Take, Self::Drop); + /// deconstruct + fn dropb(self) -> (Self::DropB, Self::TakeB); +} +/// backing trait for [`pick`] +pub trait Pick<const N: usize>: Tupl { + /// `self[N]` + type At; + /// self[..N] + type L: Tupl; + /// self[N + 1..] + type R: Tupl; + /// reconstruct + fn repick(l: Self::L, at: Self::At, r: Self::R) -> Self; + /// deconstruct + fn depict(self) -> (Self::L, Self::At, Self::R); +} +/// Main tuple trait. +pub trait Tupl: core::marker::Tuple { + /// Refer to listmonster. + type Head; + /// Refer to listmonster. + type Last; + /// Refer to listmonster. + type Tail: Tupl; + /// Refer to listmonster. + type Init: Tupl; + /// Middle section of listmonster. + type Inner: Tupl; + /// Size of tuple. + const LEN: usize; + /// Note: Partial function. + #[doc(alias = "left")] + fn head(self) -> Self::Head + where + Self: Sized, + { + self.uncons().0 + } + /// Note: Partial function. + #[doc(alias = "right")] + fn last(self) -> Self::Last + where + Self: Sized, + { + self.unsnoc().1 + } + + /// Note: Partial function. + fn take<const N: usize>(self) -> Self::Take + where + Self: TD<N> + Sized, + { + self.dropf().0 + } + /// Note: Partial function. + fn drop<const N: usize>(self) -> Self::Drop + where + Self: TD<N> + Sized, + { + self.dropf().1 + } + /// Note: Partial function. + fn take_back<const N: usize>(self) -> Self::TakeB + where + Self: TD<N> + Sized, + { + self.dropb().1 + } + /// Note: Partial function. + fn drop_back<const N: usize>(self) -> Self::DropB + where + Self: TD<N> + Sized, + { + self.dropb().0 + } + /// Gives you the element at index N (from 0). + /// Note: partial function + fn pick<const N: usize>(self) -> Self::At + where + Self: Pick<N> + Sized, + { + self.depict().1 + } + + /// unconstructs self, returning head and tail (see listmonster) + fn uncons(self) -> (Self::Head, Self::Tail); + /// untcurtsnocs self, returning init and last (see listmonster) + fn unsnoc(self) -> (Self::Init, Self::Last); + /// weird thing, full construct + fn fcons(start: Self::Head, inner: Self::Inner, last: Self::Last) -> Self; + /// reconstruct; dont use directly + fn cons(head: Self::Head, tail: Self::Tail) -> Self; + /// retcurtsnoc; dont use directly + fn snoc(init: Self::Init, last: Self::Last) -> Self; +} + +#[test] +fn t() { + 2.with(cons(4i8, (1i16, 2i32)).push(2)) + .take::<2>() + .push(4) + .push(0u8) + .pick::<3>(); +} diff --git a/src/picks.rs b/src/picks.rs new file mode 100644 index 0000000..2b862f1 --- /dev/null +++ b/src/picks.rs @@ -0,0 +1,33 @@ +use super::*; +/// second +pub fn snd<T: Tupl + Pick<{ 2 - 1 }>>(x: T) -> T::At { + x.pick::<1>() +} +/// third +pub fn thrd<T: Tupl + Pick<{ 3 - 1 }>>(x: T) -> T::At { + x.pick::<2>() +} +/// fourth +pub fn frth<T: Tupl + Pick<{ 4 - 1 }>>(x: T) -> T::At { + x.pick::<3>() +} +/// fifth +pub fn ffth<T: Tupl + Pick<{ 5 - 1 }>>(x: T) -> T::At { + x.pick::<4>() +} +/// sixth +pub fn sxth<T: Tupl + Pick<{ 6 - 1 }>>(x: T) -> T::At { + x.pick::<5>() +} +/// seventh +pub fn svnt<T: Tupl + Pick<{ 7 - 1 }>>(x: T) -> T::At { + x.pick::<6>() +} +/// eigth +pub fn eith<T: Tupl + Pick<{ 8 - 1 }>>(x: T) -> T::At { + x.pick::<7>() +} +/// ninth +pub fn nint<T: Tupl + Pick<{ 9 - 1 }>>(x: T) -> T::At { + x.pick::<8>() +} |