Browse Source

initial commit

master
Jonathan Strong 4 years ago
commit
afcb14b810
3 changed files with 354 additions and 0 deletions
  1. +3
    -0
      .gitignore
  2. +9
    -0
      Cargo.toml
  3. +342
    -0
      src/lib.rs

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
/target
Cargo.lock
*.swp

+ 9
- 0
Cargo.toml View File

@@ -0,0 +1,9 @@
[package]
name = "macro-examples"
version = "0.1.0"
authors = ["Jonathan Strong <jonathan.strong@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

+ 342
- 0
src/lib.rs View File

@@ -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<T>(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<String, u32> = 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<i32> = vec![1, 2, 3];
let ys: Vec<i32> = {
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<T: PartialOrd>(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<T: Display>(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 ;; } => {
<A as Devious>::xyz(&$x)
};

{ $x:ident <-> } => {
<A as Devious>::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);

}

}

Loading…
Cancel
Save