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 --- Cargo.lock | 4 +- Cargo.toml | 2 +- dhall_core/Cargo.toml | 2 +- dhall_core/src/parser.rs | 2 +- improved_slice_patterns/Cargo.toml | 9 ++ improved_slice_patterns/src/lib.rs | 261 +++++++++++++++++++++++++++++++++++++ iter_patterns/Cargo.toml | 9 -- iter_patterns/src/lib.rs | 261 ------------------------------------- 8 files changed, 275 insertions(+), 275 deletions(-) create mode 100644 improved_slice_patterns/Cargo.toml create mode 100644 improved_slice_patterns/src/lib.rs delete mode 100644 iter_patterns/Cargo.toml delete mode 100644 iter_patterns/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6cbb9a7..70298e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,7 +83,7 @@ name = "dhall_core" version = "0.1.0" dependencies = [ "dhall_generated_parser 0.1.0", - "iter_patterns 0.1.0", + "improved_slice_patterns 0.1.0", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -146,7 +146,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "iter_patterns" +name = "improved_slice_patterns" version = "0.1.0" [[package]] diff --git a/Cargo.toml b/Cargo.toml index fc7d003..0474d20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ "dhall_generated_parser", "dhall_core", "dhall_generator", - "iter_patterns", + "improved_slice_patterns", ] # # Parser is super slow when not optimized diff --git a/dhall_core/Cargo.toml b/dhall_core/Cargo.toml index a2b242c..bfc418a 100644 --- a/dhall_core/Cargo.toml +++ b/dhall_core/Cargo.toml @@ -12,4 +12,4 @@ doctest = false itertools = "0.8.0" pest = "2.1" dhall_generated_parser = { path = "../dhall_generated_parser" } -iter_patterns = { path = "../iter_patterns" } +improved_slice_patterns = { path = "../improved_slice_patterns" } diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs index e520f05..cae6cbd 100644 --- a/dhall_core/src/parser.rs +++ b/dhall_core/src/parser.rs @@ -171,7 +171,7 @@ macro_rules! make_parser { #[allow(unused_imports)] use ParsedValue::*; #[allow(unreachable_code)] - let res: $o = iter_patterns::match_vec!($children; + let res: $o = improved_slice_patterns::match_vec!($children; $( [$($args)*] => $body, )* [x..] => Err( format!("Unexpected children: {:?}", x.collect::>()) diff --git a/improved_slice_patterns/Cargo.toml b/improved_slice_patterns/Cargo.toml new file mode 100644 index 0000000..f4d9577 --- /dev/null +++ b/improved_slice_patterns/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "improved_slice_patterns" +version = "0.1.0" +authors = ["Nadrieril "] +license = "BSD-2-Clause" +edition = "2018" + +[lib] +doctest = false 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(); +} diff --git a/iter_patterns/Cargo.toml b/iter_patterns/Cargo.toml deleted file mode 100644 index 5a4c45f..0000000 --- a/iter_patterns/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "iter_patterns" -version = "0.1.0" -authors = ["Nadrieril "] -license = "BSD-2-Clause" -edition = "2018" - -[lib] -doctest = false diff --git a/iter_patterns/src/lib.rs b/iter_patterns/src/lib.rs deleted file mode 100644 index 0672499..0000000 --- a/iter_patterns/src/lib.rs +++ /dev/null @@ -1,261 +0,0 @@ -#![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