Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update macro tutorial (+ minor release note edit) #4123

Closed
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Update macro tutorial to recognize item/statement macros and be clearer
about invocation location vs. interpolation location.
  • Loading branch information
paulstansifer committed Dec 6, 2012
commit 01b101af61ad2905cac2c56f04f2240184301e7d
33 changes: 27 additions & 6 deletions doc/tutorial-macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,44 @@ To take as an argument a fragment of Rust code, write `$` followed by a name
* `pat` (a pattern, usually appearing in a `match` or on the left-hand side of
a declaration. Examples: `Some(t)`; `(17, 'a')`; `_`.)
* `block` (a sequence of actions. Example: `{ log(error, "hi"); return 12; }`)

The parser interprets any token that's not preceded by a `$` literally. Rust's usual
rules of tokenization apply,

So `($x:ident -> (($e:expr)))`, though excessively fancy, would designate a macro
that could be invoked like: `my_macro!(i->(( 2+2 )))`.

## Invocation location

A macro invocation may take the place of (and therefore expand to) either an
expression, an item, or a statement. The Rust parser will parse the macro
invocation as a "placeholder" for whichever of those three nonterminals is
appropriate for the location.

At expansion time, the output of the macro will be parsed as whichever of the
three nonterminals it stands in for. This means that a single macro might,
for example, expand to an item or an expression, depending on its arguments
(and cause a syntax error if it is called with the wrong argument for its
location). Although this behavior sounds excessively dynamic, it is known to
be useful under some circumstances.


# Transcription syntax

The right-hand side of the `=>` follows the same rules as the left-hand side,
except that a `$` need only be followed by the name of the syntactic fragment
to transcribe into the macro expansion; its type need not be repeated.

The right-hand side must be enclosed by delimiters, and must be
an expression. Currently, invocations of user-defined macros can only appear in a context
where the Rust grammar requires an expression, even though `macro_rules!` itself can appear
in a context where the grammar requires an item.
The right-hand side must be enclosed by delimiters, which are ignored by the
transcriber (therefore `() => ((1,2,3))` is a macro that expands to a tuple
expression, `() => (let $x=$val)` is a macro that expands to a statement, and
`() => (1,2,3)` is a macro that expands to a syntax errror).

## Interpolation location

The interpolation `$argument_name` may appear in any location consistent with
its fragment specifier (i.e., if it is specified as `ident`, it may be used
anywhere an identifier is permitted).

# Multiplicity

Expand Down Expand Up @@ -163,7 +184,7 @@ fragments by the macro parser:
fragment. For example, if the comma were omitted from the syntax of
`early_return!` above, `input_1 [` would've been interpreted as the beginning
of an array index. In fact, invoking the macro would have been impossible.
2. The parser must have eliminated all ambiguity by the time it reaches a
2. The parser must have eliminated all ambiguity by the time it reaches a
`$name:fragment_specifier` declaration. This limitation can result in parse
errors when declarations occur at the beginning of, or immediately after,
a `$(...)*`. For example, the grammar `$($t:ty)* $e:expr` will always fail to
Expand Down