macros: rename some sections, cover macro expansion (WIP)
diff --git a/examples/staging/macros/arguments/designators.rs b/examples/staging/macros/arguments/designators.rs index 0b88855..9e9f78a 100644 --- a/examples/staging/macros/arguments/designators.rs +++ b/examples/staging/macros/arguments/designators.rs
@@ -1,28 +1,28 @@ #![feature(macro_rules)] macro_rules! create_function { - // this macro takes an argument of "type" `ident` - // the `ident` designator is used for variable/function names + // This macro takes an argument of "type" `ident` + // The `ident` designator is used for variable/function names ($func_name:ident) => { - // this macro creates a function with name `$func_name` + // This macro creates a function with name `$func_name` fn $func_name() { - // the stringify! macro converts an `ident` into a string - println!("You called {}()", - stringify!($func_name)) + // The `stringify!` macro converts an `ident` into a string + println!("You called {}()", stringify!($func_name)) } } } create_function!(foo) create_function!(bar) +// Designators provide type safety in macros +//create_function!(0) +// TODO ^ Try uncommenting this line macro_rules! print_result { - // the `expr` designator is used for expressions + // The `expr` designator is used for expressions ($expression:expr) => { - // stringify! will convert the expression *as it is* into a string - println!("{} = {}", - stringify!($expression), - $expression) + // `stringify!` will convert the expression *as it is* into a string + println!("{} = {}", stringify!($expression), $expression) } } @@ -32,7 +32,7 @@ print_result!(1u + 1); - // remember that blocks are expressions + // Remember that blocks are expressions too print_result!({ let x = 1u;
diff --git a/examples/staging/macros/arguments/input.md b/examples/staging/macros/arguments/input.md index 93f7efa..ba2e8e3 100644 --- a/examples/staging/macros/arguments/input.md +++ b/examples/staging/macros/arguments/input.md
@@ -9,10 +9,10 @@ * `expr` is used for expressions * `ident` is used for variable/function names * `item` -* `matchers` (lhs of the => in macro rules) +* `matchers` * `pat` * `path` * `stmt` -* `tt` (*token tree*) is used for operators and tokens +* `tt` (*token tree*) is used for operators (`+`, `-`, etc) * `ty` (*type*)
diff --git a/examples/staging/macros/arity/input.md b/examples/staging/macros/arity/input.md new file mode 100644 index 0000000..f8c954f --- /dev/null +++ b/examples/staging/macros/arity/input.md
@@ -0,0 +1,15 @@ +Macros can have different implementations depending on their arity (number of +arguments), this can be used to implement some form of function overloading. +Here we implement a macro similar to Python's overloaded +[range](https://docs.python.org/3/library/stdtypes.html?highlight=range#range) +function: + +```rust +// Here is how it should work +range!(10i) // Returns 0..9 +range!(3i, 10) // Returns 3..9 +range!(3i, 10, 2) // Returns 3, 5, 7, 9 (3..9 in increments of 2) +``` + +{range.play} +
diff --git a/examples/staging/macros/arity/range.rs b/examples/staging/macros/arity/range.rs new file mode 100644 index 0000000..98a7025 --- /dev/null +++ b/examples/staging/macros/arity/range.rs
@@ -0,0 +1,32 @@ +#![feature(macro_rules)] + +use std::iter::range_step; + +// `macro_rules` behaves like a `match` expression +macro_rules! range ( + // The left side of the fat arrow is the `pattern` side, and the right side + // is the `expansion` side + ($end:expr) => { + range(0, $end) + }; + // The macro can have a different expansion for each arity + ($start:expr, $end:expr) => { + range($start, $end) + }; + // ^ Each pattern-match arm must be terminated by a semicolon + ($start:expr, $end:expr, $step:expr) => { + range_step($start, $end, $step) + }; + // The semicolon on the last arm is optional (is a trailing semicolon) +) + +fn main() { + for i in range!(10i) { print!("{}", i) } + println!(""); + + for i in range!(3i, 10) { print!("{}", i) } + println!(""); + + for i in range!(3i, 10, 2) { print!("{}", i) } +} +
diff --git a/examples/staging/macros/test_suite/dry.rs b/examples/staging/macros/dry/dry.rs similarity index 100% rename from examples/staging/macros/test_suite/dry.rs rename to examples/staging/macros/dry/dry.rs
diff --git a/examples/staging/macros/test_suite/input.md b/examples/staging/macros/dry/input.md similarity index 100% rename from examples/staging/macros/test_suite/input.md rename to examples/staging/macros/dry/input.md
diff --git a/examples/staging/macros/expand/input.md b/examples/staging/macros/expand/input.md new file mode 100644 index 0000000..0b967d1 --- /dev/null +++ b/examples/staging/macros/expand/input.md
@@ -0,0 +1,30 @@ +You can check the expansion of any macro by running the +`rustc --pretty expanded` command over your source file. + +Let's see what the `vec!` macro expands to: + +{vec.rs} + +``` rust +$ rustc --pretty expanded vec.rs +#![feature(phase)] +#![no_std] +#![feature(globs)] +#[phase(plugin, link)] +extern crate std; +extern crate native; +use std::prelude::*; +fn main() { + let v1 = + { let mut _temp = ::std::vec::Vec::new(); _temp.push(1u); _temp }; + let v2 = + { + let mut _temp = ::std::vec::Vec::new(); + _temp.push(1u); + _temp.push(2); + _temp + }; +} +``` + +Soon you'll learn how to define macros that work like this one!
diff --git a/examples/staging/macros/expand/vec.rs b/examples/staging/macros/expand/vec.rs new file mode 100644 index 0000000..21f5438 --- /dev/null +++ b/examples/staging/macros/expand/vec.rs
@@ -0,0 +1,4 @@ +fn main() { + let v1 = vec!(1u); + let v2 = vec!(1u, 2); +}
diff --git a/examples/staging/macros/input.md b/examples/staging/macros/input.md index 819a4a5..5d5b030 100644 --- a/examples/staging/macros/input.md +++ b/examples/staging/macros/input.md
@@ -3,7 +3,7 @@ ends with a bang `!`, but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program. -Macros are created using the `macro_rules!` macro. +Macros are defined using the `macro_rules!` macro. {simple.play}
diff --git a/examples/staging/macros/repetition/input.md b/examples/staging/macros/repetition/input.md index bcc71bb..f1cce1d 100644 --- a/examples/staging/macros/repetition/input.md +++ b/examples/staging/macros/repetition/input.md
@@ -1,51 +1,32 @@ -Macros can also match and return repeated patterns. This is similar but -different from a regex. `$($x:expr),+` is a matching example. It uses the -`$()` grouping operator to mark `x` as repeatable. `+` or `*` denotes -repetition amount; one or more and zero or more respectively. `,` -is the only separator token available. +Macros can be defined to handle indefinite arity (any number of arguments). +This can be accomplished using the *repetition* syntax and the `+` +and `*` operators. This is what the syntax looks like: -```rust -// The separator only checks separation; this is -// not a regex. -$($x:expr),+ // matches `1, 2, 3` but not `1, 2,` -``` +On the pattern side: -When returned, it uses a similar notation: `$($x:expr),+` => `$($x),+` -however, a returned list will not be re-expanded whereas, matching -will find the whole thing. +* `($($x:expr),+)`: matches a list (with at least one element) of `expr`s + separated by commas. Examples: `('a', 'b')` and `(1 + 2, 3 * 4, 5 - 6)` -```rust -#![feature(macro_rules)] +* `($($y:ident),*)`: matches a list of `ident`s separated by commas, but also + handles the zero arity case. Examples: `()` and `(foo, bar)` -macro_rules! echo_broken { - // Attempt to match and return with one compact expression - ($($x:expr),+) => { $($x),+ }; -} +* Neither of these two patterns will match a list that has a trailing comma. To + allow trailing commas, you can use this pattern: `($($z:expr),+,)` -macro_rules! echo_works { - ($x:expr) => { $x }; // one element list - // $x is the first element. `$($y:expr),+` is the tail - ($x:expr, $($y:expr),+) => { - // pass the tail to `echo_works` so it can be expanded - ($x, echo_works!($($y),+)) - }; -} +On the expansion side: -fn main() { - println!("{}", echo_broken!(1u)); // Works for single elements +* `[$($x),+]`: will expand the input arguments (the `$x`s) into an array. + Following the previous example: `['a', 'b']` and `[1 + 2, 3 * 4, 5 - 6]` - // Errors: `,` ignored. A list is never re-expanded so when - // the `,` is reached, the rest of the line is ignored. - // Rust does not allow incomplete expansion, so it errors. To - // handle this, recursion is needed. - //println!("{}", echo_broken!(1u, 2u)); +For our example, we'll make a `min!` macro that can take any number of +arguments and will return the smallest one. Under the hood it will use the +`min` function that compares *two* arguments. - println!("{}", echo_works!(1u)); - // Would like it flat but it nests the results... - println!("{}", echo_works!(1u, 2u, 3u, 4u, 5u, 6u)); -} -``` - -Another example making a min macro: {repeat.play} +If you check the expansion you'll see this expression in the place of the last +macro: + +``` rust +std::cmp::min(5u, std::cmp::min(2u * 3, 4u)) +```
diff --git a/examples/staging/macros/repetition/repeat.rs b/examples/staging/macros/repetition/repeat.rs index dbe6258..a9a1780 100644 --- a/examples/staging/macros/repetition/repeat.rs +++ b/examples/staging/macros/repetition/repeat.rs
@@ -1,16 +1,16 @@ #![feature(macro_rules)] -// min! will calculate the minimum of any number of arguments +// It's common to use recursion to handle indefinite arity macro_rules! min { // base case ($x:expr) => { $x }; - // `$x` followed by at least one `$y,` + // `$x` followed by at least another `expr` ($x:expr, $($y:expr),+) => { - // call min! on the tail `$y` + // Recursion! Call `min!` on the tail std::cmp::min($x, min!($($y),+)) - } + }; } fn main() {
diff --git a/examples/staging/macros/separators/input.md b/examples/staging/macros/separators/input.md index 82fb09b..8644cdd 100644 --- a/examples/staging/macros/separators/input.md +++ b/examples/staging/macros/separators/input.md
@@ -1,4 +1,20 @@ -Macros can be overloaded to accept different combinations of arguments. +So far we have been using a comma to separate the arguments fed into a macro, +but macros are more flexible than that! You are free to use symbols or words to +separate the input arguments of the macro. -{overload.play} +Let's create a sugary macro to initialize hash maps: +{separators.play} + +The expansion of the `map!` macro looks like this: + +``` rust +let alphabet = + { + let mut _temp = std::collections::HashMap::new(); + _temp.insert('a', "apple"); + _temp.insert('b', "banana"); + _temp.insert('c', "carrot"); + _temp + }; +```
diff --git a/examples/staging/macros/separators/overload.rs b/examples/staging/macros/separators/overload.rs deleted file mode 100644 index e1e04b2..0000000 --- a/examples/staging/macros/separators/overload.rs +++ /dev/null
@@ -1,25 +0,0 @@ -#![feature(macro_rules)] - -// macro_rules! is similar to a match block -macro_rules! test { - // the arguments don't need to be separated by a comma - // any template can be used - ($left:expr and $right:expr) => { - println!("{} and {} is {}", - stringify!($left), - stringify!($right), - $left && $right) - }; - // ^ each arm must be ended with a semicolon - ($left:expr or $right:expr) => { - println!("{} or {} is {}", - stringify!($left), - stringify!($right), - $left || $right) - }; -} - -fn main() { - test!(1i + 1 == 2i and 2i * 2 == 4i); - test!(true or false); -}
diff --git a/examples/staging/macros/separators/separators.rs b/examples/staging/macros/separators/separators.rs new file mode 100644 index 0000000..3d38c2e --- /dev/null +++ b/examples/staging/macros/separators/separators.rs
@@ -0,0 +1,26 @@ +#![feature(macro_rules)] + +macro_rules! map { + ($($key:expr => $value:expr),*) => ({ + let mut _temp = std::collections::HashMap::new(); + + $(_temp.insert($key, $value);)* + + _temp + }); + // Handle trailing commas + ($($key:expr => $value:expr),+,) => ( + map!($($key => $value),+) + ); +} + +fn main() { + let alphabet = map! { + 'a' => "apple", + 'b' => "banana", + 'c' => "carrot", + }; + + println!("{}", alphabet); +} +
diff --git a/examples/staging/macros/simple.rs b/examples/staging/macros/simple.rs index 648bc07..ac8fb9b 100644 --- a/examples/staging/macros/simple.rs +++ b/examples/staging/macros/simple.rs
@@ -1,16 +1,16 @@ -// macros are behind a feature gate +// Macros are behind a "feature gate", because they are considered experimental #![feature(macro_rules)] // This is the simplest macro, `say_hello` is the name of the macro macro_rules! say_hello { // `()` indicates that the macro takes no argument () => { - // the macro will expand into the contents of this block + // The macro will expand into the contents of this block println!("Hello!"); } } fn main() { - // this call will expand into `println!("Hello");` - say_hello!() + // This call will expand into `println!("Hello");` + say_hello!(); }
diff --git a/examples/staging/macros/variable_args/input.md b/examples/staging/macros/variable_args/input.md deleted file mode 100644 index d8d0b20..0000000 --- a/examples/staging/macros/variable_args/input.md +++ /dev/null
@@ -1,13 +0,0 @@ -Variadic (variable number of arguments) functions can be implemented via -macros. Here we implement a macro similar to Python's overloaded -[range](https://docs.python.org/3/tutorial/controlflow.html#the-range-function) function: - -```rust -// Here is how it should work -range!(10i) // Returns 0..9 -range!(3i, 10) // Returns 3..9 -range!(3i, 10, 2) // Returns 3, 5, 7, 9 (3..9 in increments of 2) -``` - -{range.play} -
diff --git a/examples/staging/macros/variable_args/range.rs b/examples/staging/macros/variable_args/range.rs deleted file mode 100644 index 5eb4205..0000000 --- a/examples/staging/macros/variable_args/range.rs +++ /dev/null
@@ -1,28 +0,0 @@ -#![feature(macro_rules)] - -use std::iter::range_step; - -// range! will take up to 3 inputs and call range -// function depending on inputs -macro_rules! range ( - ($end:expr) => { - range(0, $end) - }; - ($start:expr, $end:expr) => { - range($start, $end) - }; - ($start:expr, $end:expr, $step:expr) => { - range_step($start, $end, $step) - }; -) - -fn main() { - for i in range!(10i) { print!("{}", i) } - println!(""); - - for i in range!(3i, 10) { print!("{}", i) } - println!(""); - - for i in range!(3i, 10, 2) { print!("{}", i) } -} -
diff --git a/examples/structure.json b/examples/structure.json index 207823a..6247e8a 100644 --- a/examples/structure.json +++ b/examples/structure.json
@@ -104,11 +104,13 @@ { "id": "bench", "title": "Benchmarking", "children": null }, { "id": "ffi", "title": "Foreign Function Interface", "children": null }, { "id": "macros", "title": "macro_rules!", "children": [ - { "id": "arguments", "title": "Arguments and designators", "children": null }, - { "id": "variable_args", "title": "Variadic macros", "children": null }, - { "id": "separators", "title": "Variable separator options", "children": null }, - { "id": "repetition", "title": "Repetition and expansion", "children": null }, - { "id": "test_suite", "title": "Generating generic test suite implementations", "children": null } + { "id": "expand", "title": "Expansion", "children": null }, + { "id": "arguments", "title": "Designators", "children": null }, + { "id": "arity", "title": "Arity", "children": null }, + { "id": "repetition", "title": "Repetition", "children": null }, + { "id": "separators", "title": "Separators", "children": null }, + { "id": "dry", "title": "DRY", "children": null }, + { "id": "export", "title": "Export", "children": null } ] }, { "id": "rand", "title": "Random", "children": null }, { "id": "simd", "title": "SIMD", "children": null },