@@ 1,4 1,4 @@
-#[allow(unused_macros)]
+#[macro_export]
macro_rules! match_case {
// ==== GO! ====
($e:expr, @go {_}, $body:expr, $else:expr) => {
@@ 32,13 32,12 @@ macro_rules! match_case {
};
($e:expr, @go {($($lhs:tt)*)}, $body:expr, $else:expr) => {{
let r = match $e {
- $crate::Lisp::List(xs) => match_case!(xs, @munch {} {$($lhs)*}, $body, None),
+ $crate::Lisp::List(xs) => $crate::match_case!(xs, @munch {} {$($lhs)*}, Some($body), None),
_ => None,
};
- if r.is_some() {
- r
- } else {
- $else
+ match r {
+ Some(r) => r,
+ _ => $else
}
}};
@@ 47,22 46,21 @@ macro_rules! match_case {
if $es.is_empty() { $body } else { $else }
};
($es:expr, @munch {$($lhs:tt)+} {}, $body:expr, $else:expr) => {
- match_case!($es, @munch {$($lhs)+} {,}, $body, $else)
+ $crate::match_case!($es, @munch {$($lhs)+} {,}, $body, $else)
};
($es:expr, @munch {$($lhs:tt)+} {, $($lhss:tt)*}, $body:expr, $else:expr) => {{
let r = if let Some((first, rest)) = $es.split_first() {
- match_case!(first, @go {$($lhs)+}, match_case!(rest, @munch {} {$($lhss)*}, $body, None), None)
+ $crate::match_case!(first, @go {$($lhs)+}, $crate::match_case!(rest, @munch {} {$($lhss)*}, Some($body), None), None)
} else {
None
};
- if r.is_some() {
- r
- } else {
- $else
+ match r {
+ Some(r) => r,
+ _ => $else,
}
}};
($es:expr, @munch {$($lhs:tt)*} {$t:tt $($tts:tt)*}, $body:expr, $else:expr) => {
- match_case!($es, @munch {$($lhs)* $t} {$($tts)*}, $body, $else)
+ $crate::match_case!($es, @munch {$($lhs)* $t} {$($tts)*}, $body, $else)
};
}
@@ 70,22 68,45 @@ macro_rules! match_case {
macro_rules! match_expr {
// ==== MUNCH! ====
($e:expr, @munch {$($lhs:tt)*} => $body:expr, $($clauses:tt)*) => {
- match_case!($e, @go {$($lhs)*}, Some($body), match_expr!($e, @munch {} $($clauses)*))
+ $crate::match_case!($e, @go {$($lhs)*}, $body, $crate::match_expr!($e, @munch {} $($clauses)*))
};
($e:expr, @munch {$($lhs:tt)*} => $body:expr) => {
- match_expr!($e, @munch {$($lhs)*} => $body ,)
+ $crate::match_expr!($e, @munch {$($lhs)*} => $body ,)
+ };
+ ($e:expr, @munch {}) => {
+ compile_error!("Missing catch-all case")
+ };
+ ($e:expr, @munch {$($lhs:tt)*} $t:tt $($tts:tt)*) => {
+ $crate::match_expr!($e, @munch {$($lhs)* $t} $($tts)*)
+ };
+
+ // ==== ENTRY ====
+ ($e:expr, { $($clauses:tt)* }) => {{
+ let e = &$e;
+ $crate::match_expr!(e, @munch {} $($clauses)*)
+ }};
+}
+
+#[macro_export]
+macro_rules! try_match_expr {
+ // ==== MUNCH! ====
+ ($e:expr, @munch {$($lhs:tt)*} => $body:expr, $($clauses:tt)*) => {
+ $crate::match_case!($e, @go {$($lhs)*}, Some($body), $crate::try_match_expr!($e, @munch {} $($clauses)*))
+ };
+ ($e:expr, @munch {$($lhs:tt)*} => $body:expr) => {
+ $crate::try_match_expr!($e, @munch {$($lhs)*} => $body ,)
};
($e:expr, @munch {}) => {
None
};
($e:expr, @munch {$($lhs:tt)*} $t:tt $($tts:tt)*) => {
- match_expr!($e, @munch {$($lhs)* $t} $($tts)*)
+ $crate::try_match_expr!($e, @munch {$($lhs)* $t} $($tts)*)
};
// ==== ENTRY ====
($e:expr, { $($clauses:tt)* }) => {{
let e = &$e;
- match_expr!(e, @munch {} $($clauses)*)
+ $crate::try_match_expr!(e, @munch {} $($clauses)*)
}};
}
@@ 95,24 116,26 @@ mod test {
#[test]
fn match_expr() {
- assert_eq!(match_expr!((), { _ => "hello" }), Some("hello"));
+ assert_eq!(match_expr!((), { _ => "hello" }), "hello");
+
+ assert_eq!(match_expr!(Lisp::Int(1), { 0 => "zero", _ => "nonzero" }), "nonzero");
- assert_eq!(match_expr!(Lisp::List(vec![]), { () => "yo", _ => "hello" }), Some("yo"));
+ assert_eq!(match_expr!(Lisp::List(vec![]), { () => "yo", _ => "hello" }), "yo");
assert_eq!(
- match_expr!(parse(r#" ("hello" "there") "#.as_bytes()).unwrap(), { (_, s) => s.clone() }),
+ match_expr!(parse(r#" ("hello" "there") "#.as_bytes()).unwrap(), { (_, s) => Some(s.clone()), _ => None }),
Some(Lisp::String("there".to_owned()))
);
- assert_eq!(match_expr!(Lisp::Int(0), { 0 => 666, 123 => 456 }), Some(666));
- assert_eq!(match_expr!(Lisp::Int(123), { 0 => 666, 123 => 456 }), Some(456));
- assert_eq!(match_expr!(Lisp::Int(123), { i(x) => *x }), Some(123));
+ assert_eq!(match_expr!(Lisp::Int(0), { 0 => 666, 123 => 456, _ => panic!("undefined") }), 666);
+ assert_eq!(match_expr!(Lisp::Int(123), { 0 => Some(666), 123 => Some(456), _ => None }), Some(456));
+ assert_eq!(try_match_expr!(Lisp::Int(123), { i(x) => *x }), Some(123));
- assert_eq!(match_expr!(parse(b"foo").unwrap(), { sy(s) => *s }), Symbol::pack_str("foo"));
- assert_eq!(match_expr!(parse(b"foo").unwrap(), { sy(_) => () }), Some(()));
- assert_eq!(match_expr!(parse(b"foo-bar").unwrap(), { 'foo_bar => () }), Some(()));
+ assert_eq!(try_match_expr!(parse(b"foo").unwrap(), { sy(s) => *s }), Symbol::pack_str("foo"));
+ assert_eq!(try_match_expr!(parse(b"foo").unwrap(), { sy(_) => () }), Some(()));
+ assert!(match_expr!(parse(b"foo-bar").unwrap(), { 'foo_bar => true, _ => false }));
- assert_eq!(match_expr!(parse(b"(foo 123)").unwrap(), { ('foo, i(x)) => *x }), Some(123));
+ assert_eq!(try_match_expr!(parse(b"(foo 123)").unwrap(), { ('foo, i(x)) => *x }), Some(123));
#[derive(Debug, PartialEq)]
enum Update {
@@ 122,8 145,9 @@ mod test {
use Update::*;
let v = match_expr!(parse(b"(version 1 2 3)").unwrap(), {
('version, i(major), i(minor), i(patch)) => Version(*major, *minor, *patch),
- 'force => Force
+ 'force => Force,
+ e => panic!("unexpected expression {e}")
});
- assert_eq!(v, Some(Version(1, 2, 3)));
+ assert_eq!(v, Version(1, 2, 3));
}
}