rune/macros/mod.rs
1//! The macro system of Rune.
2//!
3//! Macros are registered with [Module::macro_][crate::Module::macro_] and are
4//! function-like items that are expanded at compile time.
5//!
6//! Macros take token streams as arguments and are responsible for translating
7//! them into another token stream that will be embedded into the source location
8//! where the macro was invoked.
9//!
10//! The attribute macros [`rune::macro_`](crate::macro_) for function macros (`some_macro!( ... )`) and
11//! [`rune::attribute_macro`](crate::attribute_macro) for attribute macros (`#[some_macro ...]`).
12//!
13//! There are two ways to make sense of an input. A macro which needs to know
14//! what an argument *is* parses it into a syntax tree with
15//! [`MacroContext::parser`], which is bounded by the `max-ast-depth` option
16//! since a tree is walked by recursing over it. A macro which only needs to
17//! know where each argument *ends* - which is what the standard library's own
18//! macros need - splits the input with [`MacroContext::exprs`] and passes each
19//! argument on as the tokens it was written as, which neither recurses nor
20//! holds the input to that much smaller bound.
21//!
22//! ```
23//! use rune::{T, Context, Diagnostics, Module, Vm};
24//! use rune::ast;
25//! use rune::compile;
26//! use rune::macros::{quote, MacroContext, TokenStream, ToTokens};
27//! use rune::parse::Parser;
28//! use rune::termcolor::{ColorChoice, StandardStream};
29//! use rune::alloc::String;
30//! use rune::sync::Arc;
31//!
32//! #[rune::macro_]
33//! fn concat_idents(cx: &mut MacroContext<'_, '_, '_>, input: &TokenStream) -> compile::Result<TokenStream> {
34//! let mut output = String::new();
35//!
36//! let mut p = Parser::from_token_stream(input, cx.input_span());
37//!
38//! let ident = p.parse::<ast::Ident>()?;
39//! output.try_push_str(cx.resolve(ident)?)?;
40//!
41//! while p.parse::<Option<T![,]>>()?.is_some() {
42//! if p.is_eof()? {
43//! break;
44//! }
45//!
46//! let ident = p.parse::<ast::Ident>()?;
47//! output.try_push_str(cx.resolve(ident)?)?;
48//! }
49//!
50//! p.eof()?;
51//!
52//! let output = cx.ident(&output)?;
53//! Ok(quote!(#output).into_token_stream(cx)?)
54//! }
55//!
56//! #[rune::attribute_macro]
57//! fn rename(cx: &mut MacroContext<'_, '_, '_>, input: &TokenStream, item: &TokenStream) -> compile::Result<TokenStream> {
58//! let mut parser = Parser::from_token_stream(item, cx.macro_span());
59//! let mut fun: ast::ItemFn = parser.parse_all()?;
60//!
61//! let mut parser = Parser::from_token_stream(input, cx.input_span());
62//! fun.name = parser.parse_all::<ast::EqValue<_>>()?.value;
63//!
64//! let mut tokens = TokenStream::new();
65//! fun.to_tokens(cx, &mut tokens);
66//! Ok(tokens)
67//! }
68//!
69//! let mut m = Module::new();
70//! m.macro_meta(concat_idents)?;
71//! m.macro_meta(rename)?;
72//!
73//! let mut context = Context::new();
74//! context.install(m)?;
75//!
76//! let runtime = Arc::try_new(context.runtime()?)?;
77//!
78//! let mut sources = rune::sources! {
79//! entry => {
80//! #[rename = foobar]
81//! fn renamed() {
82//! 42
83//! }
84//!
85//! pub fn main() {
86//! let foobar = foobar();
87//! concat_idents!(foo, bar)
88//! }
89//! }
90//! };
91//!
92//! let mut diagnostics = Diagnostics::new();
93//!
94//! let result = rune::prepare(&mut sources)
95//! .with_context(&context)
96//! .with_diagnostics(&mut diagnostics)
97//! .build();
98//!
99//! if !diagnostics.is_empty() {
100//! let mut writer = StandardStream::stderr(ColorChoice::Always);
101//! diagnostics.emit(&mut writer, &sources)?;
102//! }
103//!
104//! let unit = result?;
105//! let unit = Arc::try_new(unit)?;
106//!
107//! let mut vm = Vm::new(runtime, unit);
108//! let value = vm.call(["main"], ())?;
109//! let value: u32 = rune::from_value(value)?;
110//!
111//! assert_eq!(value, 42);
112//! # Ok::<_, rune::support::Error>(())
113//! ```
114
115mod format_args;
116#[doc(inline)]
117pub use self::format_args::FormatArgs;
118
119mod into_lit;
120#[doc(inline)]
121pub use self::into_lit::IntoLit;
122
123mod macro_context;
124#[cfg(feature = "std")]
125#[doc(inline)]
126pub use self::macro_context::test;
127#[doc(inline)]
128pub use self::macro_context::MacroContext;
129
130mod quote_fn;
131#[doc(inline)]
132pub use self::quote_fn::{quote_fn, Quote};
133
134mod storage;
135pub(crate) use self::storage::Storage;
136#[doc(inline)]
137pub use self::storage::{SyntheticId, SyntheticKind};
138
139mod token_stream;
140#[doc(inline)]
141pub use self::token_stream::{ToTokens, TokenStream, TokenStreamIter};
142
143/// Macro helper function for quoting the token stream as macro output.
144///
145/// Is capable of quoting everything in Rune, except for the following:
146/// * Labels, which must be created using `Label::new`.
147/// * Dynamic quoted strings and other literals, which must be created using
148/// `Lit::new`.
149///
150/// ```
151/// use rune::macros::quote;
152///
153/// quote!(hello self);
154/// ```
155///
156/// # Interpolating values
157///
158/// Values are interpolated with `#value`, or `#(value + 1)` for expressions.
159///
160/// # Iterators
161///
162/// Anything that can be used as an iterator can be iterated over with
163/// `#(iter)*`. A token can also be used to join inbetween each iteration, like
164/// `#(iter),*`.
165pub use rune_macros::quote;
166
167/// Helper derive to implement [`ToTokens`].
168pub use rune_macros::ToTokens;