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 },