diff --git a/Cargo.lock b/Cargo.lock index a4c58fd..50007b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,7 @@ version = "0.0.0" dependencies = [ "insta", "prettyplease", + "proc-macro-error2", "proc-macro2", "quote", "syn", @@ -71,6 +72,28 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74cdd32837fa2e86ec09c8266e5aad92400ac934c6dbca83d54673b298db3e45" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.86" diff --git a/Cargo.toml b/Cargo.toml index 5ffab6d..1e2fa9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,10 @@ repository = "https://github.com/hydro-project/matchbox" proc-macro = true [dependencies] +proc-macro-error2 = "2.0.0" proc-macro2 = "1.0.80" quote = "1.0.37" -syn = { version = "2.0.0", features = ["fold", "full", "extra-traits"] } +syn = { version = "2.0.7", features = ["fold", "full", "extra-traits"] } [dev-dependencies] insta = "1.40.0" diff --git a/README.md b/README.md index b7c75d6..804662f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ `Deref` patterns in `match` for stable Rust. Now you can match through `Rc`, `String`, etc. -`matchbox::match_deref!{...}` is a procedural macro, which allows you to use deref patterns right now in stable Rust. +`matchbox::matchbox!{...}` is a procedural macro, which allows you to use deref patterns right now in stable Rust. For example: ```rust,no_run @@ -15,7 +15,7 @@ enum Value { use Value::*; let v: &Value = todo!(); -matchbox::match_deref!{ +matchbox::matchbox!{ match v { Nil => todo!(), Cons(Deref @ Symbol(Deref @ "quote"), Deref @ Cons(x, Deref @ Nil)) => todo!(), @@ -34,7 +34,7 @@ The macro calls `Deref::deref` internally. Keep in mind that `Deref::deref` take Consider this code: ```rust,ignore -matchbox::match_deref!{ +matchbox::matchbox!{ match v { Symbol(Deref @ x) => { // some_code_here diff --git a/src/lib.rs b/src/lib.rs index fb739f4..4848226 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,46 +1,138 @@ #![warn(missing_docs)] #![doc = include_str!("../README.md")] +use std::str::FromStr; + +use syn::spanned::Spanned; + mod test; +struct PatSingle(syn::Pat); +impl syn::parse::Parse for PatSingle { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let pat = syn::Pat::parse_single(input)?; + Ok(Self(pat)) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u8)] +enum Type { + Owned, + Stamp, + Deref, +} +impl Type { + fn add_ref(self) -> Self { + match self { + Self::Owned => Self::Stamp, + Self::Stamp => Self::Deref, + Self::Deref => Self::Deref, + } + } + fn as_op(self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + Self::Owned => quote::quote_spanned! {span=> * }, + Self::Stamp => quote::quote_spanned! {span=> &* }, + Self::Deref => quote::quote_spanned! {span=> &** }, + } + } +} +impl FromStr for Type { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "owned" => Ok(Self::Owned), + "stamp" => Ok(Self::Stamp), + "deref" => Ok(Self::Deref), + other => Err(other.to_owned()), + } + } +} + +#[derive(Default)] struct MyFold { - binds: Vec<(syn::Ident, syn::Pat)>, + binds: Vec<(syn::Ident, syn::Pat, Type, proc_macro2::Span)>, counter: i32, + diagnostics: Vec, +} +impl MyFold { + fn handle(&mut self, subpat: syn::Pat, typ: Type, span: proc_macro2::Span) -> syn::PatIdent { + let id = syn::Ident::new( + &format!("a{}", self.counter), + subpat.span().resolved_at(proc_macro2::Span::mixed_site()), + ); + self.counter += 1; + self.binds.push((id.clone(), subpat, typ, span)); + syn::PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: id, + subpat: None, + } + } } impl syn::fold::Fold for MyFold { + fn fold_pat(&mut self, i: syn::Pat) -> syn::Pat { + if let syn::Pat::Macro(expr_macro) = i { + let span = expr_macro.mac.path.span(); + if let Some(typ @ ("deref" | "owned" | "stamp")) = expr_macro + .mac + .path + .get_ident() + .map(ToString::to_string) + .as_deref() + { + match syn::parse2::(expr_macro.mac.tokens) { + Ok(PatSingle(subpat)) => { + let typ = typ.parse().unwrap(); + let pat_ident = self.handle(subpat, typ, span); + syn::Pat::Ident(pat_ident) + } + Err(err) => { + self.diagnostics.push(err.into()); + syn::parse_quote_spanned!(span=> _error) // Error placeholder pattern. + } + } + } else { + syn::Pat::Macro(syn::fold::fold_expr_macro(self, expr_macro)) + } + } else { + syn::fold::fold_pat(self, i) + } + } + fn fold_pat_ident(&mut self, i: syn::PatIdent) -> syn::PatIdent { if i.by_ref.is_some() || i.mutability.is_some() || i.ident != "Deref" { syn::fold::fold_pat_ident(self, i) - } else if let Some(subpat) = i.subpat { - let id = syn::Ident::new( - &format!("a{}", self.counter), - proc_macro2::Span::mixed_site(), - ); - self.counter += 1; - self.binds.push((id.clone(), *subpat.1)); - syn::PatIdent { - attrs: vec![], - by_ref: None, - mutability: None, - ident: id, - subpat: None, - } + } else if let Some((_at, subpat)) = i.subpat { + self.handle(*subpat, Type::Deref, i.ident.span()) } else { syn::fold::fold_pat_ident(self, i) } } } -fn tower(binds: &[(syn::Ident, syn::Pat)], yes: syn::Expr, no: &syn::Expr) -> syn::Expr { +fn tower( + binds: &[(syn::Ident, syn::Pat, Type, proc_macro2::Span)], + yes: syn::Expr, + no: &syn::Expr, + add_ref: bool, +) -> syn::Expr { if binds.is_empty() { yes } else { - let id = &binds[0].0; - let pat = &binds[0].1; - let rec = tower(&binds[1..], yes, no); - syn::parse_quote! { - if let #pat = ::core::ops::Deref::deref(#id) { + let (ref id, ref pat, mut typ, span) = binds[0]; + let rec = tower(&binds[1..], yes, no, add_ref); + if add_ref { + typ = typ.add_ref(); + } + let op = typ.as_op(span); + syn::parse_quote_spanned! {span=> + if let #pat = #op #id { #rec } else { #no @@ -49,48 +141,45 @@ fn tower(binds: &[(syn::Ident, syn::Pat)], yes: syn::Expr, no: &syn::Expr) -> sy } } -fn do_match_deref(mut m: syn::ExprMatch) -> syn::ExprMatch { +fn matchbox_impl(mut m: syn::ExprMatch) -> syn::ExprMatch { let mut new_arms = vec![]; for mut arm in m.arms { use syn::fold::Fold; - let mut my_fold = MyFold { - binds: vec![], - counter: 0, - }; + + let span = arm.pat.span(); + let mut my_fold = MyFold::default(); arm.pat = my_fold.fold_pat(arm.pat); { + // recurse only after top layer is complete. let mut i = 0; while i < my_fold.binds.len() { let a = std::mem::replace( &mut my_fold.binds[i].1, - syn::Pat::Verbatim(Default::default()), + syn::Pat::Verbatim(quote::quote_spanned!(span=> )), // Temp placeholder ); my_fold.binds[i].1 = my_fold.fold_pat(a); i += 1; } } if !my_fold.binds.is_empty() { - if let Some((if_token, src_guard)) = arm.guard { - let t = tower(&my_fold.binds, *src_guard, &syn::parse_quote! { false }); - arm.guard = Some(( - if_token, - Box::new(syn::parse_quote! { { #[allow(unused_variables)] #t } }), - )); + let (yes, no) = if let Some((_if_token, src_guard)) = arm.guard { + (*src_guard, syn::parse_quote_spanned! {span=> false }) } else { - let t = tower( - &my_fold.binds, - syn::parse_quote! { true }, - &syn::parse_quote! { false }, - ); - arm.guard = Some(( - Default::default(), - Box::new(syn::parse_quote! { { #[allow(unused_variables)] #t } }), - )); - } + ( + syn::parse_quote_spanned! {span=> true }, + syn::parse_quote_spanned! {span=> false }, + ) + }; + let t = tower(&my_fold.binds, yes, &no, true); + arm.guard = Some(( + syn::Token![if](span), + Box::new(syn::parse_quote_spanned! {span=> { #[allow(unused_variables)] #t } }), + )); *arm.body = tower( &my_fold.binds, *arm.body, - &syn::parse_quote! { panic!("Two invocations of Deref::deref returned different outputs on same inputs") }, + &syn::parse_quote_spanned! {span=> panic!("Two invocations of Deref::deref returned different outputs on same inputs") }, + false, ); } new_arms.push(arm); @@ -101,7 +190,7 @@ fn do_match_deref(mut m: syn::ExprMatch) -> syn::ExprMatch { /// See [crate]. #[proc_macro] -pub fn match_deref(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream { - let a = do_match_deref(syn::parse_macro_input!(tokens as syn::ExprMatch)); +pub fn matchbox(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream { + let a = matchbox_impl(syn::parse_macro_input!(tokens as syn::ExprMatch)); quote::quote! { #a }.into() } diff --git a/src/snapshots/matchbox__test__basic-2.snap b/src/snapshots/matchbox__test__basic-2.snap index 2dbb75f..8a011f3 100644 --- a/src/snapshots/matchbox__test__basic-2.snap +++ b/src/snapshots/matchbox__test__basic-2.snap @@ -1,20 +1,20 @@ --- source: src/test.rs -expression: "snapshot!\n{ match () { Deref @ (Deref @ x,) => (), Deref @ (Deref @ x,) => () } }" +expression: "snapshot!\n{ match () { deref!((deref!(x),)) => {} Deref @ (Deref @ x,) => {} } }" --- fn main() { match () { a0 if { #[allow(unused_variables)] - if let (a1,) = ::core::ops::Deref::deref(a0) { - if let x = ::core::ops::Deref::deref(a1) { true } else { false } + if let (a1,) = &**a0 { + if let x = &**a1 { true } else { false } } else { false } } => { - if let (a1,) = ::core::ops::Deref::deref(a0) { - if let x = ::core::ops::Deref::deref(a1) { - () + if let (a1,) = &**a0 { + if let x = &**a1 { + {} } else { panic!( "Two invocations of Deref::deref returned different outputs on same inputs", @@ -28,15 +28,15 @@ fn main() { } a0 if { #[allow(unused_variables)] - if let (a1,) = ::core::ops::Deref::deref(a0) { - if let x = ::core::ops::Deref::deref(a1) { true } else { false } + if let (a1,) = &**a0 { + if let x = &**a1 { true } else { false } } else { false } } => { - if let (a1,) = ::core::ops::Deref::deref(a0) { - if let x = ::core::ops::Deref::deref(a1) { - () + if let (a1,) = &**a0 { + if let x = &**a1 { + {} } else { panic!( "Two invocations of Deref::deref returned different outputs on same inputs", diff --git a/src/snapshots/matchbox__test__basic-3.snap b/src/snapshots/matchbox__test__basic-3.snap index fb3a5bb..2784a56 100644 --- a/src/snapshots/matchbox__test__basic-3.snap +++ b/src/snapshots/matchbox__test__basic-3.snap @@ -1,15 +1,15 @@ --- source: src/test.rs -expression: "snapshot! { match () { Deref @ (Deref @ (Deref @ x,), Deref @ y) => () } }" +expression: "snapshot!\n{\n match ()\n {\n deref!((deref!((deref!(x),)), deref!(y))) => {} Deref @\n (Deref @ (Deref @ x,), Deref @ y) => {}\n }\n}" --- fn main() { match () { a0 if { #[allow(unused_variables)] - if let (a1, a2) = ::core::ops::Deref::deref(a0) { - if let (a3,) = ::core::ops::Deref::deref(a1) { - if let y = ::core::ops::Deref::deref(a2) { - if let x = ::core::ops::Deref::deref(a3) { true } else { false } + if let (a1, a2) = &**a0 { + if let (a3,) = &**a1 { + if let y = &**a2 { + if let x = &**a3 { true } else { false } } else { false } @@ -20,11 +20,53 @@ fn main() { false } } => { - if let (a1, a2) = ::core::ops::Deref::deref(a0) { - if let (a3,) = ::core::ops::Deref::deref(a1) { - if let y = ::core::ops::Deref::deref(a2) { - if let x = ::core::ops::Deref::deref(a3) { - () + if let (a1, a2) = &**a0 { + if let (a3,) = &**a1 { + if let y = &**a2 { + if let x = &**a3 { + {} + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } + a0 if { + #[allow(unused_variables)] + if let (a1, a2) = &**a0 { + if let (a3,) = &**a1 { + if let y = &**a2 { + if let x = &**a3 { true } else { false } + } else { + false + } + } else { + false + } + } else { + false + } + } => { + if let (a1, a2) = &**a0 { + if let (a3,) = &**a1 { + if let y = &**a2 { + if let x = &**a3 { + {} } else { panic!( "Two invocations of Deref::deref returned different outputs on same inputs", diff --git a/src/snapshots/matchbox__test__basic_owned-2.snap b/src/snapshots/matchbox__test__basic_owned-2.snap new file mode 100644 index 0000000..ec6cc85 --- /dev/null +++ b/src/snapshots/matchbox__test__basic_owned-2.snap @@ -0,0 +1,30 @@ +--- +source: src/test.rs +expression: "snapshot! { match () { owned!((owned!(x),)) => {} } }" +--- +fn main() { + match () { + a0 if { + #[allow(unused_variables)] + if let (a1,) = &*a0 { + if let x = &*a1 { true } else { false } + } else { + false + } + } => { + if let (a1,) = *a0 { + if let x = *a1 { + {} + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } + } +} diff --git a/src/snapshots/matchbox__test__basic_owned-3.snap b/src/snapshots/matchbox__test__basic_owned-3.snap new file mode 100644 index 0000000..bf98aa7 --- /dev/null +++ b/src/snapshots/matchbox__test__basic_owned-3.snap @@ -0,0 +1,50 @@ +--- +source: src/test.rs +expression: "snapshot! { match () { owned!((owned!((owned!(x),)), owned!(y))) => {} } }" +--- +fn main() { + match () { + a0 if { + #[allow(unused_variables)] + if let (a1, a2) = &*a0 { + if let (a3,) = &*a1 { + if let y = &*a2 { + if let x = &*a3 { true } else { false } + } else { + false + } + } else { + false + } + } else { + false + } + } => { + if let (a1, a2) = *a0 { + if let (a3,) = *a1 { + if let y = *a2 { + if let x = *a3 { + {} + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } + } +} diff --git a/src/snapshots/matchbox__test__basic_owned.snap b/src/snapshots/matchbox__test__basic_owned.snap new file mode 100644 index 0000000..6fbde8f --- /dev/null +++ b/src/snapshots/matchbox__test__basic_owned.snap @@ -0,0 +1,7 @@ +--- +source: src/test.rs +expression: "snapshot! { match () {} }" +--- +fn main() { + match () {} +} diff --git a/src/snapshots/matchbox__test__bindings.snap b/src/snapshots/matchbox__test__bindings.snap new file mode 100644 index 0000000..5153b8c --- /dev/null +++ b/src/snapshots/matchbox__test__bindings.snap @@ -0,0 +1,20 @@ +--- +source: src/test.rs +expression: "snapshot! { match () { a @ Deref @ b @ () => {}, _ => panic!(), } }" +--- +fn main() { + match () { + a @ a0 if { + #[allow(unused_variables)] if let b @ () = &**a0 { true } else { false } + } => { + if let b @ () = &**a0 { + {} + } else { + panic!( + "Two invocations of Deref::deref returned different outputs on same inputs", + ) + } + } + _ => panic!(), + } +} diff --git a/src/snapshots/matchbox__test__guards.snap b/src/snapshots/matchbox__test__guards.snap index 178175d..1ecaae3 100644 --- a/src/snapshots/matchbox__test__guards.snap +++ b/src/snapshots/matchbox__test__guards.snap @@ -5,10 +5,9 @@ expression: "snapshot! { match () { Deref @ x if x == x => () } }" fn main() { match () { a0 if { - #[allow(unused_variables)] - if let x = ::core::ops::Deref::deref(a0) { x == x } else { false } + #[allow(unused_variables)] if let x = &**a0 { x == x } else { false } } => { - if let x = ::core::ops::Deref::deref(a0) { + if let x = &**a0 { () } else { panic!( diff --git a/src/snapshots/matchbox__test__other-3.snap b/src/snapshots/matchbox__test__other-3.snap index 8565f6e..1496372 100644 --- a/src/snapshots/matchbox__test__other-3.snap +++ b/src/snapshots/matchbox__test__other-3.snap @@ -6,14 +6,10 @@ fn main() { match () { a0 if { #[allow(unused_variables)] - if let a1 = ::core::ops::Deref::deref(a0) { - if let x = ::core::ops::Deref::deref(a1) { true } else { false } - } else { - false - } + if let a1 = &**a0 { if let x = &**a1 { true } else { false } } else { false } } => { - if let a1 = ::core::ops::Deref::deref(a0) { - if let x = ::core::ops::Deref::deref(a1) { + if let a1 = &**a0 { + if let x = &**a1 { () } else { panic!( diff --git a/src/snapshots/matchbox__test__other-4.snap b/src/snapshots/matchbox__test__other-4.snap index bfbbf4f..091ede4 100644 --- a/src/snapshots/matchbox__test__other-4.snap +++ b/src/snapshots/matchbox__test__other-4.snap @@ -4,11 +4,8 @@ expression: "snapshot! { match &Rc::new(Nil) { Deref @ _ => a0, _ => panic!() } --- fn main() { match &Rc::new(Nil) { - a0 if { - #[allow(unused_variables)] - if let _ = ::core::ops::Deref::deref(a0) { true } else { false } - } => { - if let _ = ::core::ops::Deref::deref(a0) { + a0 if { #[allow(unused_variables)] if let _ = &**a0 { true } else { false } } => { + if let _ = &**a0 { a0 } else { panic!( diff --git a/src/snapshots/matchbox__test__other.snap b/src/snapshots/matchbox__test__other.snap index 9674344..96b8bc2 100644 --- a/src/snapshots/matchbox__test__other.snap +++ b/src/snapshots/matchbox__test__other.snap @@ -4,11 +4,8 @@ expression: "snapshot! { match () { Deref @ x => () } }" --- fn main() { match () { - a0 if { - #[allow(unused_variables)] - if let x = ::core::ops::Deref::deref(a0) { true } else { false } - } => { - if let x = ::core::ops::Deref::deref(a0) { + a0 if { #[allow(unused_variables)] if let x = &**a0 { true } else { false } } => { + if let x = &**a0 { () } else { panic!( diff --git a/src/snapshots/matchbox__test__spelling.snap b/src/snapshots/matchbox__test__spelling.snap index 9674344..96b8bc2 100644 --- a/src/snapshots/matchbox__test__spelling.snap +++ b/src/snapshots/matchbox__test__spelling.snap @@ -4,11 +4,8 @@ expression: "snapshot! { match () { Deref @ x => () } }" --- fn main() { match () { - a0 if { - #[allow(unused_variables)] - if let x = ::core::ops::Deref::deref(a0) { true } else { false } - } => { - if let x = ::core::ops::Deref::deref(a0) { + a0 if { #[allow(unused_variables)] if let x = &**a0 { true } else { false } } => { + if let x = &**a0 { () } else { panic!( diff --git a/src/test.rs b/src/test.rs index 70d7cd7..c053d23 100644 --- a/src/test.rs +++ b/src/test.rs @@ -3,7 +3,7 @@ macro_rules! snapshot { ( $( $x:tt )* ) => { { - let match_expr = crate::do_match_deref(::syn::parse_quote! { + let match_expr = crate::matchbox_impl(::syn::parse_quote! { $( $x )* }); ::prettyplease::unparse(&::syn::parse_quote! { @@ -35,15 +35,38 @@ fn test_basic() { // Every arm starts with 0 snapshot_test! { match () { - Deref @ (Deref @ x,) => (), - Deref @ (Deref @ x,) => () + deref!((deref!(x),)) => {} + Deref @ (Deref @ x,) => {} } }; // More difficult test snapshot_test! { match () { - Deref @ (Deref @ (Deref @ x,), Deref @ y) => () + deref!((deref!((deref!(x),)), deref!(y))) => {} + Deref @ (Deref @ (Deref @ x,), Deref @ y) => {} + } + }; +} + +#[test] +fn test_basic_owned() { + snapshot_test! { + match () { + } + } + + // Every arm starts with 0 + snapshot_test! { + match () { + owned!((owned!(x),)) => {} + } + }; + + // More difficult test + snapshot_test! { + match () { + owned!((owned!((owned!(x),)), owned!(y))) => {} } }; } @@ -65,7 +88,7 @@ fn test_spelling() { #[test] fn test_other() { - // match_deref! doesn't insert unneded guard "if true" + // matchbox! doesn't insert unneded guard "if true" snapshot_test! { match () { Deref @ x => () @@ -90,6 +113,16 @@ fn test_other() { }; } +#[test] +fn test_bindings() { + snapshot_test! { + match () { + a @ Deref @ b @ () => {}, + _ => panic!(), + } + } +} + #[test] fn test_guards() { // Guards diff --git a/tests/test.rs b/tests/test.rs index 9f55033..c43dcb1 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,97 +1,183 @@ +#![deny(unused_variables)] + +#[derive(PartialEq, Eq, Debug, Clone)] +enum LispValue { + Nil, + Cons(Box, Box), + Symbol(String), +} +use LispValue::*; + #[test] fn tests() { - #[deny(unused_variables)] - { - use std::rc::Rc; - #[derive(PartialEq, Eq, Debug, Clone)] - enum LispValue { - Nil, - Cons(Rc, Rc), - Symbol(String), + let _: i32 = matchbox::matchbox! { + match &Nil { + Nil => 0, + Cons(Deref @ _, Deref @ Nil) => 0, + Cons(Deref @ Symbol(Deref @ "a"), _) => 0, + Cons(_, _) => 0, + Symbol(_) => 0, } - use LispValue::*; - let _: i32 = matchbox::match_deref! { - match &Nil { - Nil => 0, - Cons(Deref @ _, Deref @ Nil) => 0, - Cons(Deref @ Symbol(Deref @ "a"), _) => 0, - Cons(_, _) => 0, - Symbol(_) => 0, + }; + assert_eq!( + matchbox::matchbox! { + match &Cons(Box::new(Symbol("a".to_owned())), Box::new(Nil)) { + Cons(Deref @ Symbol(Deref @ "a"), _) => 1, + _ => 0 } - }; - assert_eq!( - matchbox::match_deref! { - match &Cons(Rc::new(Symbol("a".to_owned())), Rc::new(Nil)) { - Cons(Deref @ Symbol(Deref @ "a"), _) => 1, + }, + 1 + ); + assert_eq!( + matchbox::matchbox! { + match &Cons(Box::new(Symbol("a".to_owned())), Box::new(Nil)) { + Cons(Deref @ Symbol(Deref @ "b"), _) => 1, + _ => 0 + } + }, + 0 + ); + assert_eq!( + matchbox::matchbox! { + match &Cons(Box::new(Symbol("a".to_owned())), Box::new(Nil)) { + Cons(Deref @ Symbol(Deref @ "a"), Deref @ x) => x, + _ => panic!() + } + }, + &Nil + ); + assert_eq!( + (|| { + let _: i32 = matchbox::matchbox! { + match &Cons(Box::new(Symbol("a".to_owned())), Box::new(Nil)) { + Cons(Deref @ Symbol(Deref @ "a"), Deref @ x) => return x.clone(), _ => 0 } - }, - 1 - ); + }; + panic!(); + })(), + Nil + ); + { + let a0 = 0; assert_eq!( - matchbox::match_deref! { - match &Cons(Rc::new(Symbol("a".to_owned())), Rc::new(Nil)) { - Cons(Deref @ Symbol(Deref @ "b"), _) => 1, - _ => 0 + matchbox::matchbox! { + match &Box::new(Nil) { + Deref @ _ => a0, + _ => panic!() } }, 0 ); - assert_eq!( - matchbox::match_deref! { - match &Cons(Rc::new(Symbol("a".to_owned())), Rc::new(Nil)) { - Cons(Deref @ Symbol(Deref @ "a"), Deref @ x) => x, - _ => panic!() - } + } + matchbox::matchbox! { + match &Cons(Box::new(Nil), Box::new(Nil)) { + Cons(a @ deref!(b @ Nil), _) => { + assert_eq!(a, &Box::new(Nil)); + assert_eq!(b, &Nil); }, - &Nil - ); - assert_eq!( - (|| { - let _: i32 = matchbox::match_deref! { - match &Cons(Rc::new(Symbol("a".to_owned())), Rc::new(Nil)) { - Cons(Deref @ Symbol(Deref @ "a"), Deref @ x) => return x.clone(), - _ => 0 - } - }; - panic!(); - })(), - Nil - ); - { - let a0 = 0; - assert_eq!( - matchbox::match_deref! { - match &Rc::new(Nil) { - Deref @ _ => a0, - _ => panic!() - } - }, - 0 - ); + _ => panic!(), } - matchbox::match_deref! { - match &Cons(Rc::new(Nil), Rc::new(Nil)) { - Cons(a @ Deref @ b @ Nil, _) => { - assert_eq!(a, &Rc::new(Nil)); - assert_eq!(b, &Nil); + } + assert_eq!( + matchbox::matchbox! { + match &Cons(Box::new(Cons(Box::new(Nil), Box::new(Nil))), Box::new(Nil)) { + Cons(Deref @ a, _) => matchbox::matchbox! { + match a { + Cons(Deref @ Nil, _) => 5, + _ => panic!(), + } }, _ => panic!(), } + }, + 5 + ); +} + +#[test] +fn tests_owned() { + let _: i32 = matchbox::matchbox! { + match Nil { + Nil => 0, + Cons(owned!(_), owned!(Nil)) => 0, + Cons(owned!(Symbol(stamp!("a"))), _) => 0, + Cons(_, _) => 0, + Symbol(_) => 0, } + }; + assert_eq!( + matchbox::matchbox! { + match Cons(Box::new(Symbol("a".to_owned())), Box::new(Nil)) { + Cons(owned!(Symbol(stamp!("a"))), _) => 1, + _ => 0 + } + }, + 1 + ); + assert_eq!( + matchbox::matchbox! { + match Cons(Box::new(Symbol("a".to_owned())), Box::new(Nil)) { + Cons(owned!(Symbol(stamp!("b"))), _) => 1, + _ => 0 + } + }, + 0 + ); + assert_eq!( + matchbox::matchbox! { + match Cons(Box::new(Symbol("a".to_owned())), Box::new(Nil)) { + Cons(owned!(Symbol(stamp!("a"))), owned!(x)) => x, + _ => panic!() + } + }, + Nil + ); + assert_eq!( + (|| { + let _: i32 = matchbox::matchbox! { + match Cons(Box::new(Symbol("a".to_owned())), Box::new(Nil)) { + Cons(owned!(Symbol(stamp!("a"))), owned!(x)) => return x.clone(), + _ => 0 + } + }; + panic!(); + })(), + Nil + ); + { + let a0 = 0; assert_eq!( - matchbox::match_deref! { - match &Cons(Rc::new(Cons(Rc::new(Nil), Rc::new(Nil))), Rc::new(Nil)) { - Cons(Deref @ a, _) => matchbox::match_deref! { - match a { - Cons(Deref @ Nil, _) => 5, - _ => panic!(), - } - }, + matchbox::matchbox! { + match Box::new(Nil) { + owned!(_) => a0, _ => panic!(), } }, - 5 + 0 ); } + // matchbox::matchbox! { + // match &Cons(Box::new(Nil), Box::new(Nil)) { + // Cons(a @ Deref @ b @ Nil, _) => { + // assert_eq!(a, &Box::new(Nil)); + // assert_eq!(b, &Nil); + // }, + // _ => panic!(), + // } + // } + assert_eq!( + matchbox::matchbox! { + match Cons(Box::new(Cons(Box::new(Nil), Box::new(Nil))), Box::new(Nil)) { + Cons(owned!(a), _) => matchbox::matchbox! { + match a { + Cons(owned!(Nil), _) => 5, + _ => panic!(), + } + }, + _ => panic!(), + } + }, + 5 + ); }