From afcb14b8105c49636cb3946709866c23cf889788 Mon Sep 17 00:00:00 2001 From: Jonathan Strong Date: Thu, 26 Mar 2020 02:53:38 -0400 Subject: [PATCH] initial commit --- .gitignore | 3 + Cargo.toml | 9 ++ src/lib.rs | 342 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46f3847 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +*.swp diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6c68a51 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "macro-examples" +version = "0.1.0" +authors = ["Jonathan Strong "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cbb8037 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,342 @@ +#![allow(unused)] +//#![recursion_limit = "1000000000"] + +//! # Examples +//! +//! ```compile_fail +//! macro_rules! ah_ah_ah_didnt_say_the_magic_word { +//! ($x:ident) => { +//! let x_mut_1 = &mut $x; +//! let x_mut_2 = &mut $x; +//! *x_mut_1 += 1i32; +//! } +//! } +//! let mut x: i32 = 0; +//! ah_ah_ah_didnt_say_the_magic_word!(x); +//! ``` +//! +//! Generic `min` won't compile: +//! +//! ```compile_fail +//! fn min(a: T, b: T) -> T { +//! if a < b { a } else { b } +//! } +//! ``` +//! +//! ```compile_fail +//! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } +//! let b: std::collections::HashMap = Default::default(); +//! add_one!(b); +//! ``` +//! +//! ```compile_fail +//! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } +//! add_one(42i32); +//! ``` +//! +//! ```compile_fail +//! macro_rules! make_adder_fn { +//! ($n:expr) => { +//! fn add_$n(rhs: i32) -> i32 { +//! $n + rhs +//! } +//! } +//! } +//! make_adder_fn!(42); +//! ``` + +#[cfg(test)] +mod tests { + + #[test] + fn a_bunch_of_print_lns_showing_var_args() { + println!(); + println!("{}", 1); + println!("{} {}", 1, 2); + println!("{} {} {}", 1, 2, 3); + } + + #[test] + fn shows_vec_macro_and_its_equivalent() { + let xs: Vec = vec![1, 2, 3]; + let ys: Vec = { + let mut out = Vec::with_capacity(3); + out.push(1); + out.push(2); + out.push(3); + out + }; + assert_eq!(xs, ys); + } + + #[test] + fn macro_that_if_used_would_generate_illegal_code_is_itself_not_illegal() { + macro_rules! ah_ah_ah_didnt_say_the_magic_word { + ($x:ident) => { + let x_mut_1 = &mut $x; + let x_mut_2 = &mut $x; + *x_mut_1 += 1i32; + } + } + } + + #[test] + fn generic_max_macro_compiles_and_works() { + macro_rules! max { + ($a:expr, $b:expr) => { + if $a > $b { $a } else { $b } + } + } + + let a: usize = 1; + let b: usize = 2; + assert_eq!(max!(a, b), 2); + + let c = f64::INFINITY; + let d = f64::NEG_INFINITY; + assert_eq!(max!(c, d), f64::INFINITY); + } + + #[test] + fn min_with_partial_ord_will_compile() { + fn min_that_compiles(a: T, b: T) -> T { + if a < b { a } else { b } + } + let a: usize = 1; + let b: usize = 2; + assert_eq!(min_that_compiles(a, b), 1); + } + + #[test] + fn generated_type_errors_the_example_of_something_that_works() { + macro_rules! add_one { + ($x:ident) => { + $x + 1i32 + } + } + let a: i32 = 42; + + assert_eq!(add_one!(a), 43); // Ok: expands to i32 + i32 + } + + #[test] + fn im_curious_if_a_randomly_chosen_example_from_little_quote_unquote_black_book_compiles() { + macro_rules! abacus { + ((- $($moves:tt)*) -> (+ $($count:tt)*)) => { + abacus!(($($moves)*) -> ($($count)*)) + }; + ((- $($moves:tt)*) -> ($($count:tt)*)) => { + abacus!(($($moves)*) -> (- $($count)*)) + }; + ((+ $($moves:tt)*) -> (- $($count:tt)*)) => { + abacus!(($($moves)*) -> ($($count)*)) + }; + ((+ $($moves:tt)*) -> ($($count:tt)*)) => { + abacus!(($($moves)*) -> (+ $($count)*)) + }; + + // Check if the final result is zero. + (() -> ()) => { true }; + (() -> ($($count:tt)+)) => { false }; + } + + // this one never finishes + //macro_rules! abacus2 { + // (-) => {-1}; + // (+) => {1}; + // ($($moves:tt)*) => { + // 0 $(+ abacus2!($moves))* + // } + //} + + let equals_zero = abacus!((++-+-+++--++---++----+) -> ()); + assert_eq!(equals_zero, true); + + //let equals_zero = abacus2!((++-+-+++--++---++----+) -> ()); + //assert_eq!(equals_zero, true); + } + + #[test] + fn how_about_this_example_from_little_book() { + macro_rules! parse_unitary_variants { + (@as_expr $e:expr) => {$e}; + (@as_item $($i:item)+) => {$($i)+}; + + // Exit rules. + ( + @collect_unitary_variants ($callback:ident ( $($args:tt)* )), + ($(,)*) -> ($($var_names:ident,)*) + ) => { + parse_unitary_variants! { + @as_expr + $callback!{ $($args)* ($($var_names),*) } + } + }; + + ( + @collect_unitary_variants ($callback:ident { $($args:tt)* }), + ($(,)*) -> ($($var_names:ident,)*) + ) => { + parse_unitary_variants! { + @as_item + $callback!{ $($args)* ($($var_names),*) } + } + }; + + // Consume an attribute. + ( + @collect_unitary_variants $fixed:tt, + (#[$_attr:meta] $($tail:tt)*) -> ($($var_names:tt)*) + ) => { + parse_unitary_variants! { + @collect_unitary_variants $fixed, + ($($tail)*) -> ($($var_names)*) + } + }; + + // Handle a variant, optionally with an with initialiser. + ( + @collect_unitary_variants $fixed:tt, + ($var:ident $(= $_val:expr)*, $($tail:tt)*) -> ($($var_names:tt)*) + ) => { + parse_unitary_variants! { + @collect_unitary_variants $fixed, + ($($tail)*) -> ($($var_names)* $var,) + } + }; + + // Abort on variant with a payload. + ( + @collect_unitary_variants $fixed:tt, + ($var:ident $_struct:tt, $($tail:tt)*) -> ($($var_names:tt)*) + ) => { + const _error: () = "cannot parse unitary variants from enum with non-unitary variants"; + }; + + // Entry rule. + (enum $name:ident {$($body:tt)*} => $callback:ident $arg:tt) => { + parse_unitary_variants! { + @collect_unitary_variants + ($callback $arg), ($($body)*,) -> () + } + }; + } + + /* + + not sure how to invoke this one + + let mut variants: Vec<&'static str> = Vec::new(); + + macro_rules! get_unitary_variant_str { + {() ($($var_names),*) } => { $( variants.push(stringify!($variant)); )* } + } + + parse_unitary_variants!( + enum Abcs { + A, + B, + C, + D, + E + } => get_unitary_variant_str () + ); + */ + } + + #[test] + fn tt_muncher() { + macro_rules! mixed_rules { + () => {}; + (trace $name:ident; $($tail:tt)*) => { + { + println!(concat!(stringify!($name), " = {:?}"), $name); + mixed_rules!($($tail)*); + } + }; + (trace $name:ident = $init:expr; $($tail:tt)*) => { + { + let $name = $init; + println!(concat!(stringify!($name), " = {:?}"), $name); + mixed_rules!($($tail)*); + } + }; + } + + mixed_rules!(trace x = 1;); + } + + use std::fmt::Display; + + fn i_need_display_not_debug(x: T) -> String { + format!("{}", x) + } + + #[test] + fn debug_display_macro_actually_works() { + #[derive(Debug)] + struct Point { + pub x: f64, + pub y: f64, + } + + macro_rules! debug_display { + ($t:ident) => { + impl std::fmt::Display for $t { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } + } + } + } + + debug_display!(Point); // poof! it implements Display + + let p = Point { x: 1.234, y: 2.345 }; + + i_need_display_not_debug(p); + } + + #[test] + fn example_for_why_you_should_use_fully_qualified_names() { + struct A; + + trait Devious { + fn abc(&self) -> isize { 42 } + fn xyz(&self) -> isize { 42 } + } + + impl Devious for A {} + + let a = A; + + macro_rules! didnt_see_it_coming { + { $x:ident ;; } => { + ::xyz(&$x) + }; + + { $x:ident <-> } => { + ::abc(&$x) + } + } + + assert_eq!(didnt_see_it_coming!{ a <-> }, 42); + + /// muhaha + fn the_upside_down(a: &A) -> isize { + /// we have our own 'Devious' down here... + trait Devious { + fn abc(&self) -> isize { -42 } + fn xyz(&self) -> isize { -42 } + } + + impl Devious for A {} + + didnt_see_it_coming!{ a ;; } + } + + assert_eq!(the_upside_down(&a), -42); + + } + +}