Currently we have a powerful macro system, but custom control structures stick out like a sore thumb due to the extra set of delimiters that are required.
Here is an example of a custom for
-like construct with some added
functionality:
#[feature(macro_rules)];
macro_rules! iter(
($x:pat <- .. $hi:expr $body:expr) => (
iter!($x <- range(0, $hi) $body)
);
($x:pat <- $lo:expr .. $hi:expr $body:expr) => (
iter!($x <- range($lo, $hi) $body)
);
($x:pat <- $it:expr $body:expr) => ({
let mut it = $it;
let mut curr = it.next();
loop {
match curr {
Some($x) => $body,
None => break,
}
curr = it.next();
}
});
($n:expr $body:expr) => (
iter!(_ <- ..$n $body)
);
)
iter!(10 {
print!("hi ");
})
iter!(x <- ..10 {
print!("{} ", x);
})
iter!(x <- 3..10 {
print!("{} ", x);
})
let xs = ["bananas", "in", "pajamas"];
iter!(x <- xs.iter() {
print!("{} ", *x);
})
I propose altering the macro parser to remove the need for the extra delimiters:
#[feature(macro)];
macro! iter {
{$x:pat <- .. $hi:expr $body:block} => {
iter! $x <- range(0, $hi) $body
}
{$x:pat <- $lo:expr .. $hi:expr $body:block} => {
iter! $x <- range($lo, $hi) $body
}
{$x:pat <- $it:expr $body:block)} => {{
let mut it = $it;
let mut curr = it.next();
loop {
match curr {
Some($x) => $body,
None => break,
}
curr = it.next();
}
}}
{$n:expr $body:expr} => {
iter! _ <- ..$n $body
};
}
iter! 10 {
print!("hi ");
}
iter! x <- ..10 {
print!("{} ", x);
}
iter! x <- 3..10 {
print!("{} ", x);
}
let xs = ["bananas", "in", "pajamas"];
iter! x <- xs.iter() {
print!("{} ", *x);
}
This would also have the side-affect of making macro_rules! <ident> { ... }
look much less magical.
macro_rules!
->macro!
.- Delimiters surrounding
macro!
body changed from()
to{}
. - Delimiters surrounding rules should accept
{}
only. At the moment they can be either{}
or()
. - No semi-colons needed between rules.
The parser would stop consuming tokens once a closing delimiter or the EOF was reached, throwing an error only if no rule could be completed.
macro! expr_muncher {
{$($munched:expr)*} => {}
}
{
println!("Hello!");
expr_muncher!
println!("oh noes!"); // dead code
println!("we've been eaten!"); // dead code
}
println!("Hi!");
macro_rules! assert(
($cond:expr) => {
if !$cond { fail!("assertion failed: {:s}", stringify!($cond)) }
};
($cond:expr, $msg:expr) => {
if !$cond { fail!($msg) }
};
($cond:expr, $( $arg:expr ),+) => {
if !$cond { fail!( $($arg),+ ) }
}
)
macro! assert {
{($cond:expr)} => {
if !$cond { fail!("assertion failed: {:s}", stringify!($cond)) }
}
{($cond:expr, $msg:expr)} => {
if !$cond { fail!($msg) }
}
{($cond:expr, $( $arg:expr ),+)} => {
if !$cond { fail!( $($arg),+ ) }
}
}
Notice the extra delimiters needed in the rule pattern.
- The
expr_muncher!
example is a little worrying. - Most macros written at the moment make sense looking like function-calls. The added delimiter for the rules could be seen as excessively ugly.