Skip to content

Latest commit

 

History

History
194 lines (187 loc) · 4.75 KB

20.marco-programming.md

File metadata and controls

194 lines (187 loc) · 4.75 KB

Macro-programming -> Writing custom macro

-Why write one? - sometimes inline code is more optimized, still we cannot have it to be written everytime in code. - So since macro's get expanded compile time. They can be treated as blocks which can be added, letting compiler expand on runtime.

macro_rules! a_marco {
    () => (
        println!("Oh My Macro.!")
    )
}
// macro with params
macro_rules! x_and_y {
    (x => $e:expr) => (println!("X: {}", $e));
    (y => $e:expr) => (println!("Y: {}", $e));
    // expr is an expression
}
fn main() {
    a_marco!();
    x_and_y!(x=>10);
    x_and_y!(y=>20+30);
}
  • Another simple thought, where macro's can be used are in building DSL's, since one can write LISPy code using macro
  • some custom marco example
macro_rules! build_fn {
    ($func_name:ident) => (
        fn $func_name(){
            println!("you called {:?}()", stringify!($func_name));
        }
    )
}
macro_rules! print_ex {
    ($e:expr) => (
        println!("{:?} = {:?}", stringify!($e), $e);
    )
}
fn main() {
    build_fn!(Hi_there);
    Hi_there();
    print_ex!({
        let y = 20;
        let z= 40;
        z+y+20*78+10
    })
}
  • macro with the pattern matching
macro_rules! exame {
    ($l:expr; and $r:expr) => (
        println!("{:?} and {:?} is {:?}",
        stringify!($l),
        stringify!($r),
        $l && $r)
    );
    ($l:expr; or $r:expr) => (
        println!("{:?} and {:?} is {:?}",
        stringify!($l),
        stringify!($r),
        $l || $r)
    );
    ($l:expr; something $r:expr) => (
        println!("{:?} something {:?} is {:?}",
        stringify!($l),
        stringify!($r),
        $l || $r)
    );
}
fn main() {
    exame!(1==1; and 2==1+1);
    exame!(1==1; something 2==1+1);
}
// Note: something is not a language keyword neither a variable
  • macro for magical expansions: Python like list comprehension
macro_rules! compr {
    ($id1: ident | $id2: ident <- [$start: expr; $end: expr] , $cond: expr)=>{
        {
            let mut vec = Vec::new();
            for num in $start..$end + 1 {
                if $cond(num) {
                    vec.push(num);
                }
            }
            vec
        }
    };
}
fn even(x: i32) -> bool { x % 2 == 0 }

fn odd(x: i32) -> bool { x % 2 != 0 }

fn main() {
    let evens = compr![x | x <- [1;10], even];
    println!("{:?}", evens);
    let odds = compr![y | y <- [1;10], odd];
    println!("{:?}", odds);
}
  • find min macro
// `find_min!` will calculate the minimum of any number of arguments.
macro_rules! find_min {
    // Base case:
    ($x:expr) => ($x);
    // `$x` followed by at least one `$y,`
    ($x:expr, $($y:expr),+) => (
        // Call `find_min!` on the tail `$y`
        std::cmp::min($x, find_min!($($y),+))
    )
}

fn main() {
    println!("{}", find_min!(1));
    println!("{}", find_min!(1+2, 2));
    println!("{}", find_min!(5, -2, 4));
}
  • DSL example - Simple calculator
macro_rules! calculate {
    (eval $e:expr) => {{
        {
            let val: usize = $e; // Force types to be integers
            println!("{} = {}", stringify!{$e}, val);
        }
    }};
}

fn main() {
    calculate! {
        eval 1 + 2 // hehehe `eval` is _not_ a Rust keyword!
    }

    calculate! {
        eval (1 + 2) * (3 / 4)
    }
}
  • complex variadic calculator DSL
macro_rules! calculate {
    // The pattern for a single `eval`
    (eval $e:expr) => {{
        {
            let val: usize = $e; // Force types to be integers
            println!("{} = {}", stringify!{$e}, val);
        }
    }};

    // Decompose multiple `eval`s recursively
    (eval $e:expr, $(eval $es:expr),+) => {{
        calculate! { eval $e }
        calculate! { $(eval $es),+ }
    }};
}

fn main() {
    calculate! { // Look ma! Variadic `calculate!`!
        eval 1 + 2,
        eval 3 + 4,
        eval (2 * 3) + 1
    }
}
  • some concepts/learnings from here
  • meta-variables for meta programming
  • Building a hashmap shorthand macro using macro
use std::collections::HashMap;
macro_rules! hash_map {
    ($($key: expr => $val: expr),*) => { //, after before * allows us to put , in usage between values
        {
            let mut map = HashMap::new();
            $(
                map.insert($key, $val);
            )*
            map
        }
    }
}
fn main() {
    let map = hash_map!{
        "one"=>1,
        "two"=>2,
        "three"=>3
    };
    println!("{:?}",map)
}
// *, + are wild card used in variadic expressions
  • fully functional macro lisp here