From 3ce64f46a79855913a7cc8626c45781cb6b16300 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 19 Mar 2019 21:59:41 +0100 Subject: Extract the core of parser macros into a separate crate --- iter_patterns/Cargo.toml | 8 ++ iter_patterns/src/lib.rs | 190 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 iter_patterns/Cargo.toml create mode 100644 iter_patterns/src/lib.rs (limited to 'iter_patterns') diff --git a/iter_patterns/Cargo.toml b/iter_patterns/Cargo.toml new file mode 100644 index 0000000..d63669d --- /dev/null +++ b/iter_patterns/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "iter_patterns" +version = "0.1.0" +authors = ["Nadrieril "] +edition = "2018" + +[lib] +doctest = false diff --git a/iter_patterns/src/lib.rs b/iter_patterns/src/lib.rs new file mode 100644 index 0000000..75fe51c --- /dev/null +++ b/iter_patterns/src/lib.rs @@ -0,0 +1,190 @@ +#![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 0, $iter:expr, ($body:expr), $x:ident.., $($rest:tt)*) => { + $crate::destructure_iter!(@match 1, $iter, ({ + let $x = $iter; + $body + }), $($rest)*) + }; + // Variable length pattern without a binder + (@match 0, $iter:expr, ($body:expr), .., $($rest:tt)*) => { + $crate::destructure_iter!(@match 1, $iter, ($body), $($rest)*) + }; + // Single item pattern + (@match 0, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { + if let Some($x) = $iter.next() { + $crate::destructure_iter!(@match 0, $iter, ($body), $($rest)*) + } else { + None + } + }; + // Single item pattern after a variable length one: declare reversed and take from the end + (@match 1, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { + $crate::destructure_iter!(@match 1, $iter, ( + if let Some($x) = $iter.next_back() { + $body + } else { + None + } + ), $($rest)*) + }; + + // Check no elements remain + (@match 0, $iter:expr, ($body:expr) $(,)*) => { + if $iter.next().is_some() { + None + } else { + $body + } + }; + // After a variable length pattern, everything has already been consumed + (@match 1, $iter:expr, ($body:expr) $(,)*) => { + $body + }; + + ($iter:expr; [$($args:tt)*] => $body:expr) => { + { + #[allow(unused_mut)] + let mut iter = $iter; + $crate::destructure_iter!(@match 0, iter, (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 { + ($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() { + $( + [$($args)*] => { + // Actually consume the values + #[allow(unused_mut)] + let mut iter = vec.into_iter(); + $crate::destructure_iter!(iter; [$($args)*] => $body) + } + )* + _ => None, + } + } + }; +} + + +/* Pattern-match on an iterator using the syntax of slice_patterns. + * Wraps the match body in `Some` if there was a match; returns + * `None` otherwise. + * + * Example: + * ``` + * let vec = vec![Some(1), Some(2), None]; + * + * match_iter!(vec.into_iter(); + * [Some(x), y.., z] => { + * // x: usize + * // y: impl Iterator> + * // z: Option + * } + * [x, Some(0)] => { + * // x: Option + * }, + * [..] => { + * ) + * ``` + * +*/ +#[macro_export] +macro_rules! match_iter { + ($arg:expr; $($args:tt)*) => { + { + let vec: Vec<_> = $arg.collect(); + $crate::match_vec!(vec, $($args)*) + } + }; +} + + +#[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)] => y - z, + [] => 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)]), -9); + assert_eq!(test(vec![None]), 3); + assert_eq!(test(vec![]), 0); + assert_eq!(test(vec![Some(0)]), -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 640a36906361ef5ef98fb66fd37246f084739d25 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2019 00:44:27 +0100 Subject: Handle simple parsing cases with new macros --- iter_patterns/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'iter_patterns') diff --git a/iter_patterns/src/lib.rs b/iter_patterns/src/lib.rs index 75fe51c..14ed4c9 100644 --- a/iter_patterns/src/lib.rs +++ b/iter_patterns/src/lib.rs @@ -138,7 +138,7 @@ macro_rules! match_vec { * // x: usize * // y: impl Iterator> * // z: Option - * } + * }, * [x, Some(0)] => { * // x: Option * }, @@ -152,7 +152,7 @@ macro_rules! match_iter { ($arg:expr; $($args:tt)*) => { { let vec: Vec<_> = $arg.collect(); - $crate::match_vec!(vec, $($args)*) + $crate::match_vec!(vec; $($args)*) } }; } -- cgit v1.2.3 From 4cf23db82c37f4d24ea093e268bb96a1ae1afe3d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2019 18:59:25 +0100 Subject: Obsolete old parser macros; performance is now dead --- iter_patterns/src/lib.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'iter_patterns') diff --git a/iter_patterns/src/lib.rs b/iter_patterns/src/lib.rs index 14ed4c9..5395755 100644 --- a/iter_patterns/src/lib.rs +++ b/iter_patterns/src/lib.rs @@ -25,27 +25,27 @@ #[macro_export] macro_rules! destructure_iter { // Variable length pattern - (@match 0, $iter:expr, ($body:expr), $x:ident.., $($rest:tt)*) => { - $crate::destructure_iter!(@match 1, $iter, ({ + (@match_forwards, $iter:expr, ($body:expr), $x:ident.., $($rest:tt)*) => { + $crate::destructure_iter!(@match_backwards, $iter, ({ let $x = $iter; $body }), $($rest)*) }; // Variable length pattern without a binder - (@match 0, $iter:expr, ($body:expr), .., $($rest:tt)*) => { - $crate::destructure_iter!(@match 1, $iter, ($body), $($rest)*) + (@match_forwards, $iter:expr, ($body:expr), .., $($rest:tt)*) => { + $crate::destructure_iter!(@match_backwards, $iter, ($body), $($rest)*) }; // Single item pattern - (@match 0, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { + (@match_forwards, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { if let Some($x) = $iter.next() { - $crate::destructure_iter!(@match 0, $iter, ($body), $($rest)*) + $crate::destructure_iter!(@match_forwards, $iter, ($body), $($rest)*) } else { None } }; // Single item pattern after a variable length one: declare reversed and take from the end - (@match 1, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { - $crate::destructure_iter!(@match 1, $iter, ( + (@match_backwards, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { + $crate::destructure_iter!(@match_backwards, $iter, ( if let Some($x) = $iter.next_back() { $body } else { @@ -55,7 +55,7 @@ macro_rules! destructure_iter { }; // Check no elements remain - (@match 0, $iter:expr, ($body:expr) $(,)*) => { + (@match_forwards, $iter:expr, ($body:expr) $(,)*) => { if $iter.next().is_some() { None } else { @@ -63,7 +63,7 @@ macro_rules! destructure_iter { } }; // After a variable length pattern, everything has already been consumed - (@match 1, $iter:expr, ($body:expr) $(,)*) => { + (@match_backwards, $iter:expr, ($body:expr) $(,)*) => { $body }; @@ -71,12 +71,11 @@ macro_rules! destructure_iter { { #[allow(unused_mut)] let mut iter = $iter; - $crate::destructure_iter!(@match 0, iter, (Some($body)), $($args)*,) + $crate::destructure_iter!(@match_forwards, iter, (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. @@ -124,7 +123,6 @@ macro_rules! match_vec { }; } - /* Pattern-match on an iterator using the syntax of slice_patterns. * Wraps the match body in `Some` if there was a match; returns * `None` otherwise. @@ -157,7 +155,6 @@ macro_rules! match_iter { }; } - #[test] fn test() { let test = |v: Vec>| { -- cgit v1.2.3