From d29f163e03f170a06d700ff0f62a10a489d2f058 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Apr 2019 22:45:22 +0200 Subject: Rename iter_patterns to improved_slice_patterns --- improved_slice_patterns/src/lib.rs | 261 +++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 improved_slice_patterns/src/lib.rs (limited to 'improved_slice_patterns/src') diff --git a/improved_slice_patterns/src/lib.rs b/improved_slice_patterns/src/lib.rs new file mode 100644 index 0000000..0672499 --- /dev/null +++ b/improved_slice_patterns/src/lib.rs @@ -0,0 +1,261 @@ +#![feature(slice_patterns)] + +/* Destructure an iterator using the syntax of slice_patterns. + * Wraps the match body in `Some` if there was a match; returns + * `None` otherwise. + * Contrary to slice_patterns, this allows moving out + * of the iterator. + * A variable length pattern (`x..`) is only allowed as the last + * pattern, unless the iterator is double-ended. + * + * Example: + * ``` + * let vec = vec![Some(1), Some(2), None]; + * + * destructure_iter!(vec.into_iter(); + * [Some(x), y.., z] => { + * // x: usize + * // y: impl Iterator> + * // z: Option + * } + * ) + * ``` + * +*/ +#[macro_export] +macro_rules! destructure_iter { + // Variable length pattern + (@match_forwards, $iter:expr, ($body:expr), $x:ident.., $($rest:tt)*) => { + $crate::destructure_iter!(@match_backwards, + $iter, + ({ + let $x = $iter; + $body + }), + $($rest)* + ) + }; + // Special variable length pattern with a common unary variant + (@match_forwards, $iter:expr, ($body:expr), + $variant:ident ($x:ident).., $($rest:tt)*) => { + $crate::destructure_iter!(@match_backwards, + $iter, + ({ + let $x = $iter + .map(|x| match x { + $variant(y) => y, + _ => unreachable!(), + }); + $body + }), + $($rest)* + ) + }; + // Variable length pattern without a binder + (@match_forwards, $iter:expr, ($body:expr), .., $($rest:tt)*) => { + $crate::destructure_iter!(@match_backwards, + $iter, + ($body), + $($rest)* + ) + }; + // Single item pattern + (@match_forwards, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { + if let std::option::Option::Some($x) = $iter.next() { + $crate::destructure_iter!(@match_forwards, + $iter, + ($body), + $($rest)* + ) + } else { + std::option::Option::None + } + }; + // Single item pattern after a variable length one: declare reversed and take from the end + (@match_backwards, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { + $crate::destructure_iter!(@match_backwards, $iter, ( + if let std::option::Option::Some($x) = $iter.next_back() { + $body + } else { + std::option::Option::None + } + ), $($rest)*) + }; + + // Check no elements remain + (@match_forwards, $iter:expr, ($body:expr) $(,)*) => { + if $iter.next().is_some() { + std::option::Option::None + } else { + $body + } + }; + // After a variable length pattern, everything has already been consumed + (@match_backwards, $iter:expr, ($body:expr) $(,)*) => { + $body + }; + + ($iter:expr; [$($args:tt)*] => $body:expr) => { + { + #[allow(unused_mut)] + let mut iter = $iter; + $crate::destructure_iter!(@match_forwards, + iter, + (std::option::Option::Some($body)), + $($args)*, + ) + } + }; +} + +/* Pattern-match on a vec using the syntax of slice_patterns. + * Wraps the match body in `Some` if there was a match; returns + * `None` otherwise. + * A variable length pattern (`x..`) returns an iterator. + * + * Example: + * ``` + * let vec = vec![Some(1), Some(2), None]; + * + * match_vec!(vec; + * [Some(x), y.., z] => { + * // x: usize + * // y: impl Iterator> + * // z: Option + * } + * [x, Some(0)] => { + * // x: Option + * }, + * [..] => { } + * ) + * ``` + * +*/ +#[macro_export] +macro_rules! match_vec { + // Variable length pattern + (@make_pat; ($($acc:tt)*), $x:ident.., $($rest:tt)*) => { + $crate::match_vec!(@make_pat; + ($($acc)*, $x..), + $($rest)* + ) + }; + // Special variable length pattern with a common unary variant + (@make_pat; ($($acc:tt)*), $variant:ident ($x:ident).., $($rest:tt)*) => { + $crate::match_vec!(@make_pat; + ($($acc)*, $x..), + $($rest)* + ) + }; + // Variable length pattern without a binder + (@make_pat; ($($acc:tt)*), .., $($rest:tt)*) => { + $crate::match_vec!(@make_pat; + ($($acc)*, ..), + $($rest)* + ) + }; + // Single item pattern + (@make_pat; ($($acc:tt)*), $x:pat, $($rest:tt)*) => { + $crate::match_vec!(@make_pat; + ($($acc)*, $x), + $($rest)* + ) + }; + (@make_pat; (, $($acc:tt)*), $(,)*) => { + [$($acc)*] + }; + (@make_pat; ($($acc:tt)*), $(,)*) => { + [$($acc)*] + }; + + (@make_filter; $x:ident.., $($rest:tt)*) => { + $crate::match_vec!(@make_filter; + $($rest)* + ) + }; + (@make_filter; $variant:ident ($x:ident).., $($rest:tt)*) => { + { + // Circumvent https://github.com/rust-lang/rust/issues/59803 + let is_all_variant = || $x.iter() + .all(|x| match x { + $variant(_) => true, + _ => false, + }); + is_all_variant() + } + && + $crate::match_vec!(@make_filter; + $($rest)* + ) + }; + (@make_filter; .., $($rest:tt)*) => { + $crate::match_vec!(@make_filter; + $($rest)* + ) + }; + (@make_filter; $x:pat, $($rest:tt)*) => { + $crate::match_vec!(@make_filter; + $($rest)* + ) + }; + (@make_filter; $(,)*) => { + true + }; + + ($arg:expr; $( [$($args:tt)*] => $body:expr ),* $(,)*) => { + { + let vec = $arg; + // Match as references to decide which branch to take + // I think `match_default_bindings` should make this always work but + // there may be some patterns this doesn't capture. + #[allow(unused_variables, unreachable_patterns)] + match vec.as_slice() { + $( + $crate::match_vec!(@make_pat; (), $($args)*,) + if + $crate::match_vec!(@make_filter; $($args)*,) + => { + // Actually consume the values + #[allow(unused_mut)] + let mut iter = vec.into_iter(); + $crate::destructure_iter!(iter; [$($args)*] => $body) + } + )* + _ => std::option::Option::None, + } + } + }; +} + +#[test] +fn test() { + let test = |v: Vec>| { + match_vec!(v.into_iter(); + [Some(_x), None, None] => 4, + [Some(_x), None] => 2, + [None, Some(y)] => 1, + [None, _y..] => 3, + [_x.., Some(y), Some(z), None] => y - z, + [Some(ys)..] => ys.sum(), + [] => 0, + [..] => -1, + ) + .unwrap() + }; + + assert_eq!(test(vec![Some(0), None, None]), 4); + assert_eq!(test(vec![Some(0), None]), 2); + assert_eq!(test(vec![None, Some(0)]), 1); + assert_eq!(test(vec![Some(1), Some(2), Some(5), Some(14), None]), -9); + assert_eq!(test(vec![Some(1), Some(2), Some(3), Some(4)]), 10); + assert_eq!(test(vec![None]), 3); + assert_eq!(test(vec![]), 0); + assert_eq!(test(vec![Some(0), None, Some(1)]), -1); + + // Test move out of pattern + struct Foo; + let _: (Foo, Foo) = match_vec!(vec![Some(Foo), Some(Foo)].into_iter(); + [Some(f1), Some(f2)] => (f1, f2), + ) + .unwrap(); +} -- cgit v1.2.3 From 2e78bf6f5d7cf315aa1cc4fb7f828a6ee1f66b3f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Apr 2019 22:48:53 +0200 Subject: improved_slice_patterns: use doc comments --- improved_slice_patterns/src/lib.rs | 90 +++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 45 deletions(-) (limited to 'improved_slice_patterns/src') diff --git a/improved_slice_patterns/src/lib.rs b/improved_slice_patterns/src/lib.rs index 0672499..1842599 100644 --- a/improved_slice_patterns/src/lib.rs +++ b/improved_slice_patterns/src/lib.rs @@ -1,27 +1,27 @@ #![feature(slice_patterns)] -/* Destructure an iterator using the syntax of slice_patterns. - * Wraps the match body in `Some` if there was a match; returns - * `None` otherwise. - * Contrary to slice_patterns, this allows moving out - * of the iterator. - * A variable length pattern (`x..`) is only allowed as the last - * pattern, unless the iterator is double-ended. - * - * Example: - * ``` - * let vec = vec![Some(1), Some(2), None]; - * - * destructure_iter!(vec.into_iter(); - * [Some(x), y.., z] => { - * // x: usize - * // y: impl Iterator> - * // z: Option - * } - * ) - * ``` - * -*/ +/// Destructure an iterator using the syntax of slice_patterns. +/// Wraps the match body in `Some` if there was a match; returns +/// `None` otherwise. +/// Contrary to slice_patterns, this allows moving out +/// of the iterator. +/// A variable length pattern (`x..`) is only allowed as the last +/// pattern, unless the iterator is double-ended. +/// +/// Example: +/// ``` +/// let vec = vec![Some(1), Some(2), None]; +/// +/// destructure_iter!(vec.into_iter(); +/// [Some(x), y.., z] => { +/// // x: usize +/// // y: impl Iterator> +/// // z: Option +/// } +/// ) +/// ``` +/// +/// #[macro_export] macro_rules! destructure_iter { // Variable length pattern @@ -108,29 +108,29 @@ macro_rules! destructure_iter { }; } -/* Pattern-match on a vec using the syntax of slice_patterns. - * Wraps the match body in `Some` if there was a match; returns - * `None` otherwise. - * A variable length pattern (`x..`) returns an iterator. - * - * Example: - * ``` - * let vec = vec![Some(1), Some(2), None]; - * - * match_vec!(vec; - * [Some(x), y.., z] => { - * // x: usize - * // y: impl Iterator> - * // z: Option - * } - * [x, Some(0)] => { - * // x: Option - * }, - * [..] => { } - * ) - * ``` - * -*/ +/// Pattern-match on a vec using the syntax of slice_patterns. +/// Wraps the match body in `Some` if there was a match; returns +/// `None` otherwise. +/// A variable length pattern (`x..`) returns an iterator. +/// +/// Example: +/// ``` +/// let vec = vec![Some(1), Some(2), None]; +/// +/// match_vec!(vec; +/// [Some(x), y.., z] => { +/// // x: usize +/// // y: impl Iterator> +/// // z: Option +/// } +/// [x, Some(0)] => { +/// // x: Option +/// }, +/// [..] => { } +/// ) +/// ``` +/// +/// #[macro_export] macro_rules! match_vec { // Variable length pattern -- cgit v1.2.3 From 2954e502abb274b87ae3dbbb80ef523ebe602b28 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Apr 2019 23:00:01 +0200 Subject: improved_slice_patterns: doctests --- improved_slice_patterns/src/lib.rs | 40 +++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'improved_slice_patterns/src') diff --git a/improved_slice_patterns/src/lib.rs b/improved_slice_patterns/src/lib.rs index 1842599..726b57a 100644 --- a/improved_slice_patterns/src/lib.rs +++ b/improved_slice_patterns/src/lib.rs @@ -10,15 +10,22 @@ /// /// Example: /// ``` -/// let vec = vec![Some(1), Some(2), None]; +/// use improved_slice_patterns::destructure_iter; /// -/// destructure_iter!(vec.into_iter(); +/// let vec = vec![Some(1), Some(2), Some(3), None]; +/// +/// let res = destructure_iter!(vec.into_iter(); /// [Some(x), y.., z] => { /// // x: usize /// // y: impl Iterator> /// // z: Option +/// (x, y.collect::>(), z) /// } -/// ) +/// ); +/// +/// assert_eq!(res, Some((1, vec![Some(2), Some(3)], None))); +/// +/// # Ok::<(), ()>(()) /// ``` /// /// @@ -115,19 +122,24 @@ macro_rules! destructure_iter { /// /// Example: /// ``` -/// let vec = vec![Some(1), Some(2), None]; +/// #![feature(slice_patterns)] +/// use improved_slice_patterns::match_vec; /// -/// match_vec!(vec; -/// [Some(x), y.., z] => { -/// // x: usize -/// // y: impl Iterator> -/// // z: Option -/// } -/// [x, Some(0)] => { -/// // x: Option +/// let vec = vec![Some(1), Some(2), Some(3), None]; +/// +/// let res = match_vec!(vec; +/// [Some(_), y.., None] => { +/// y.collect::>() /// }, -/// [..] => { } -/// ) +/// [None, None] => { +/// vec![] +/// }, +/// [..] => vec![] +/// ); +/// +/// assert_eq!(res, Some(vec![Some(2), Some(3)])); +/// +/// # Ok::<(), ()>(()) /// ``` /// /// -- cgit v1.2.3 From 6b60e0ded951faa6aff5091300307f5e4b139d8e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Apr 2019 23:05:52 +0200 Subject: improved_slice_patterns: Improve docs --- improved_slice_patterns/src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'improved_slice_patterns/src') diff --git a/improved_slice_patterns/src/lib.rs b/improved_slice_patterns/src/lib.rs index 726b57a..56547f9 100644 --- a/improved_slice_patterns/src/lib.rs +++ b/improved_slice_patterns/src/lib.rs @@ -1,5 +1,10 @@ #![feature(slice_patterns)] +//! A tiny crate that provides two macros to help matching +//! on `Vec`s and iterators using [`slice_patterns`][slice_patterns] +//! +//! [slice_patterns]: https://doc.rust-lang.org/nightly/unstable-book/language-features/slice-patterns.html + /// Destructure an iterator using the syntax of slice_patterns. /// Wraps the match body in `Some` if there was a match; returns /// `None` otherwise. @@ -118,6 +123,8 @@ macro_rules! destructure_iter { /// Pattern-match on a vec using the syntax of slice_patterns. /// Wraps the match body in `Some` if there was a match; returns /// `None` otherwise. +/// Contrary to slice_patterns, this allows moving out +/// of the `Vec`. /// A variable length pattern (`x..`) returns an iterator. /// /// Example: @@ -139,6 +146,20 @@ macro_rules! destructure_iter { /// /// assert_eq!(res, Some(vec![Some(2), Some(3)])); /// +/// +/// let vec = vec![Some(1), Some(2), Some(3), None]; +/// +/// let res = match_vec!(vec; +/// [Some(_), y.., Some(_)] => { +/// y.collect::>() +/// }, +/// [None, None] => { +/// vec![] +/// }, +/// ); +/// +/// assert_eq!(res, None); // there was no match +/// /// # Ok::<(), ()>(()) /// ``` /// -- cgit v1.2.3 From 74e59290247a7201c17dd748b7f554716ad16691 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Apr 2019 23:14:45 +0200 Subject: improved_slice_patterns: prepare for publishing --- improved_slice_patterns/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'improved_slice_patterns/src') diff --git a/improved_slice_patterns/src/lib.rs b/improved_slice_patterns/src/lib.rs index 56547f9..3fdd9c9 100644 --- a/improved_slice_patterns/src/lib.rs +++ b/improved_slice_patterns/src/lib.rs @@ -1,7 +1,8 @@ #![feature(slice_patterns)] //! A tiny crate that provides two macros to help matching -//! on `Vec`s and iterators using [`slice_patterns`][slice_patterns] +//! on `Vec`s and iterators using the syntax of +//! [`slice_patterns`][slice_patterns] //! //! [slice_patterns]: https://doc.rust-lang.org/nightly/unstable-book/language-features/slice-patterns.html @@ -14,7 +15,7 @@ /// pattern, unless the iterator is double-ended. /// /// Example: -/// ``` +/// ```edition2018 /// use improved_slice_patterns::destructure_iter; /// /// let vec = vec![Some(1), Some(2), Some(3), None]; @@ -128,7 +129,7 @@ macro_rules! destructure_iter { /// A variable length pattern (`x..`) returns an iterator. /// /// Example: -/// ``` +/// ```edition2018 /// #![feature(slice_patterns)] /// use improved_slice_patterns::match_vec; /// -- cgit v1.2.3 From ee3f9d270bcdbb06ad11daa61667c694196d46f8 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 14 Apr 2019 23:26:03 +0200 Subject: improved_slice_patterns: various tweaks for crates.io --- improved_slice_patterns/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'improved_slice_patterns/src') diff --git a/improved_slice_patterns/src/lib.rs b/improved_slice_patterns/src/lib.rs index 3fdd9c9..3e459bb 100644 --- a/improved_slice_patterns/src/lib.rs +++ b/improved_slice_patterns/src/lib.rs @@ -1,4 +1,5 @@ #![feature(slice_patterns)] +#![doc(html_root_url = "https://docs.rs/improved_slice_patterns/1.0.1")] //! A tiny crate that provides two macros to help matching //! on `Vec`s and iterators using the syntax of @@ -7,10 +8,13 @@ //! [slice_patterns]: https://doc.rust-lang.org/nightly/unstable-book/language-features/slice-patterns.html /// Destructure an iterator using the syntax of slice_patterns. +/// /// Wraps the match body in `Some` if there was a match; returns /// `None` otherwise. +/// /// Contrary to slice_patterns, this allows moving out /// of the iterator. +/// /// A variable length pattern (`x..`) is only allowed as the last /// pattern, unless the iterator is double-ended. /// @@ -122,10 +126,13 @@ macro_rules! destructure_iter { } /// Pattern-match on a vec using the syntax of slice_patterns. +/// /// Wraps the match body in `Some` if there was a match; returns /// `None` otherwise. +/// /// Contrary to slice_patterns, this allows moving out /// of the `Vec`. +/// /// A variable length pattern (`x..`) returns an iterator. /// /// Example: -- cgit v1.2.3