Skip to main content

rune/macros/
macro_context.rs

1//! Context for a macro.
2
3use core::fmt;
4
5use crate::alloc;
6use crate::ast;
7use crate::ast::Span;
8use crate::compile::{self, ErrorKind, ItemMeta};
9use crate::indexing::Indexer;
10use crate::internal_macros::resolve_context;
11use crate::macros::{IntoLit, ToTokens, TokenStream};
12use crate::parse::{Parse, Parser, Resolve};
13use crate::runtime::Value;
14use crate::{Source, SourceId};
15
16/// Construct an empty macro context which can be used for testing.
17///
18/// # Examples
19///
20/// ```
21/// use rune::ast;
22/// use rune::macros;
23///
24/// macros::test(|cx| {
25///     let lit = cx.lit("hello world")?;
26///     assert!(matches!(lit, ast::Lit::Str(..)));
27///     Ok(())
28/// })?;
29/// # Ok::<_, rune::support::Error>(())
30/// ```
31#[cfg(feature = "std")]
32#[cfg_attr(rune_docsrs, doc(cfg(feature = "std")))]
33pub fn test<F, O>(f: F) -> crate::support::Result<O>
34where
35    F: FnOnce(&mut MacroContext<'_, '_, '_>) -> crate::support::Result<O>,
36{
37    use rust_alloc::rc::Rc;
38
39    use crate::compile::{NoopCompileVisitor, NoopSourceLoader, Pool, Prelude, UnitBuilder};
40    use crate::hir;
41    use crate::indexing::{IndexItem, Items, Scopes};
42    use crate::macros::Storage;
43    use crate::query::Query;
44    use crate::shared::{Consts, Gen};
45    use crate::support::Context as _;
46    use crate::{Context, Diagnostics, Item, Options, Sources};
47
48    let mut unit = UnitBuilder::default();
49    let prelude = Prelude::default();
50    let gen = Gen::default();
51    let const_arena = hir::Arena::new();
52    let mut consts = Consts::default();
53    let mut storage = Storage::default();
54    let mut sources = Sources::default();
55    let mut pool = Pool::new().context("Failed to allocate pool")?;
56    let mut visitor = NoopCompileVisitor::new();
57    let mut diagnostics = Diagnostics::default();
58    let mut source_loader = NoopSourceLoader::default();
59    let options = Options::from_default_env()?;
60    let context = Context::default();
61    let mut inner = Default::default();
62
63    let mut query = Query::new(
64        &mut unit,
65        &prelude,
66        &const_arena,
67        &mut consts,
68        &mut storage,
69        &mut sources,
70        &mut pool,
71        &mut visitor,
72        &mut diagnostics,
73        &mut source_loader,
74        &options,
75        &[],
76        &gen,
77        &context,
78        &mut inner,
79    );
80
81    let source_id = SourceId::empty();
82
83    let (root_id, root_mod_id) = query
84        .insert_root_mod(source_id, Span::empty())
85        .context("Failed to inserted root module")?;
86
87    let item_meta = query
88        .item_for("root item", root_id)
89        .context("Just inserted item meta does not exist")?;
90
91    let tree = Rc::default();
92
93    let mut idx = Indexer {
94        q: query.borrow(),
95        source_id,
96        items: Items::new(Item::new()).context("Failed to construct items")?,
97        scopes: Scopes::new().context("Failed to build indexer scopes")?,
98        item: IndexItem::new(root_mod_id, root_id),
99        nested_item: None,
100        macro_depth: 0,
101        root: None,
102        queue: None,
103        loaded: None,
104        tree: &tree,
105    };
106
107    let mut cx = MacroContext {
108        macro_span: Span::empty(),
109        input_span: Span::empty(),
110        item_meta,
111        idx: &mut idx,
112    };
113
114    f(&mut cx)
115}
116
117/// Context for a running macro.
118pub struct MacroContext<'a, 'b, 'arena> {
119    /// Macro span of the full macro call.
120    pub(crate) macro_span: Span,
121    /// Macro span of the input.
122    pub(crate) input_span: Span,
123    /// The item where the macro is being evaluated.
124    pub(crate) item_meta: ItemMeta,
125    /// Indexer.
126    pub(crate) idx: &'a mut Indexer<'b, 'arena>,
127}
128
129impl<'a, 'b, 'arena> MacroContext<'a, 'b, 'arena> {
130    /// Construct a parser over a token stream, bounded by the compiler options
131    /// this macro is being expanded under.
132    ///
133    /// The syntax tree this parser produces is walked by recursing over it, so
134    /// how deep it is allowed to get is bounded by the `max-ast-depth` option.
135    /// A parser built with [`Parser::from_token_stream`] instead uses that
136    /// option's default, since it has no way of seeing what the compiler was
137    /// configured with.
138    ///
139    /// `span` is the span to use if the stream is empty - typically
140    /// [`MacroContext::input_span`].
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// # use rune::support::*;
146    /// use rune::ast;
147    /// use rune::macros::{self, quote};
148    ///
149    /// macros::test(|cx| {
150    ///     let stream = quote!(1 + 2).into_token_stream(cx)?;
151    ///
152    ///     let mut p = cx.parser(&stream, cx.input_span());
153    ///     let expr = p.parse_all::<ast::Expr>()?;
154    ///     let value = cx.eval(&expr)?;
155    ///
156    ///     let integer = value.as_integer::<u32>().context("Expected integer")?;
157    ///     assert_eq!(3, integer);
158    ///     Ok(())
159    /// })?;
160    /// # Ok::<_, rune::support::Error>(())
161    /// ```
162    pub fn parser<'s>(&self, token_stream: &'s TokenStream, span: Span) -> Parser<'s> {
163        Parser::from_token_stream(token_stream, span)
164            .with_max_depth(self.idx.q.options.max_ast_depth)
165    }
166
167    /// Evaluate the given target as a constant expression.
168    ///
169    /// # Panics
170    ///
171    /// This will panic if it's called outside of a macro context.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// # use rune::support::*;
177    /// use rune::ast;
178    /// use rune::macros::{self, quote};
179    /// use rune::parse::{Parser};
180    ///
181    /// macros::test(|cx| {
182    ///     let stream = quote!(1 + 2).into_token_stream(cx)?;
183    ///
184    ///     let mut p = Parser::from_token_stream(&stream, cx.input_span());
185    ///     let expr = p.parse_all::<ast::Expr>()?;
186    ///     let value = cx.eval(&expr)?;
187    ///
188    ///     let integer = value.as_integer::<u32>().context("Expected integer")?;
189    ///     assert_eq!(3, integer);
190    ///     Ok(())
191    /// })?;
192    /// # Ok::<_, rune::support::Error>(())
193    /// ```
194    pub fn eval(&mut self, target: &ast::Expr) -> compile::Result<Value> {
195        crate::compile::const_eval::eval_ast(self, target)
196    }
197
198    /// Construct a new literal from within a macro context.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// use rune::ast;
204    /// use rune::macros;
205    ///
206    /// macros::test(|cx| {
207    ///     let lit = cx.lit("hello world")?;
208    ///     assert!(matches!(lit, ast::Lit::Str(..)));
209    ///     Ok(())
210    /// })?;
211    /// # Ok::<_, rune::support::Error>(())
212    /// ```
213    pub fn lit<T>(&mut self, lit: T) -> alloc::Result<ast::Lit>
214    where
215        T: IntoLit,
216    {
217        T::into_lit(lit, self)
218    }
219
220    /// Construct a new identifier from the given string from inside of a macro
221    /// context.
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// use rune::ast;
227    /// use rune::macros;
228    ///
229    /// macros::test(|cx| {
230    ///     let lit = cx.ident("foo")?;
231    ///     assert!(matches!(lit, ast::Ident { .. }));
232    ///     Ok(())
233    /// })?;
234    /// # Ok::<_, rune::support::Error>(())
235    /// ```
236    pub fn ident(&mut self, ident: &str) -> alloc::Result<ast::Ident> {
237        let span = self.macro_span();
238        let id = self.idx.q.storage.insert_str(ident)?;
239        let source = ast::LitSource::Synthetic(id);
240        Ok(ast::Ident { span, source })
241    }
242
243    /// Construct a new label from the given string. The string should be
244    /// specified *without* the leading `'`, so `"foo"` instead of `"'foo"`.
245    ///
246    /// This constructor does not panic when called outside of a macro context
247    /// but requires access to a `span` and `storage`.
248    ///
249    /// # Examples
250    ///
251    /// ```
252    /// use rune::ast;
253    /// use rune::macros;
254    ///
255    /// macros::test(|cx| {
256    ///     let lit = cx.label("foo")?;
257    ///     assert!(matches!(lit, ast::Label { .. }));
258    ///     Ok(())
259    /// })?;
260    /// # Ok::<_, rune::support::Error>(())
261    /// ```
262    pub fn label(&mut self, label: &str) -> alloc::Result<ast::Label> {
263        let span = self.macro_span();
264        let id = self.idx.q.storage.insert_str(label)?;
265        let source = ast::LitSource::Synthetic(id);
266        Ok(ast::Label { span, source })
267    }
268
269    /// Stringify the token stream.
270    pub fn stringify<T>(&mut self, tokens: &T) -> alloc::Result<Stringify<'_, 'a, 'b, 'arena>>
271    where
272        T: ToTokens,
273    {
274        let mut stream = TokenStream::new();
275        tokens.to_tokens(self, &mut stream)?;
276        Ok(Stringify { cx: self, stream })
277    }
278
279    /// Resolve the value of a token.
280    pub fn resolve<'r, T>(&'r self, item: T) -> compile::Result<T::Output>
281    where
282        T: Resolve<'r>,
283    {
284        item.resolve(resolve_context!(self.idx.q))
285    }
286
287    /// Access a literal source as a string.
288    pub(crate) fn literal_source(&self, source: ast::LitSource, span: Span) -> Option<&str> {
289        match source {
290            ast::LitSource::Text(source_id) => self.idx.q.sources.source(source_id, span),
291            ast::LitSource::Synthetic(id) => self.idx.q.storage.get_string(id),
292            ast::LitSource::BuiltIn(builtin) => Some(builtin.as_str()),
293        }
294    }
295
296    /// Insert the given source so that it has a [SourceId] that can be used in
297    /// combination with parsing functions such as
298    /// [parse_source][MacroContext::parse_source].
299    pub fn insert_source(&mut self, name: &str, source: &str) -> alloc::Result<SourceId> {
300        self.idx.q.sources.insert(Source::new(name, source)?)
301    }
302
303    /// Parse the given input as the given type that implements
304    /// [Parse][crate::parse::Parse].
305    pub fn parse_source<T>(&self, id: SourceId) -> compile::Result<T>
306    where
307        T: Parse,
308    {
309        let source = self.idx.q.sources.get(id).ok_or_else(|| {
310            compile::Error::new(Span::empty(), ErrorKind::MissingSourceId { source_id: id })
311        })?;
312
313        crate::parse::parse_all(source.as_str(), id, false)
314    }
315
316    /// The span of the macro call including the name of the macro.
317    ///
318    /// If the macro call was `stringify!(a + b)` this would refer to the whole
319    /// macro call.
320    pub fn macro_span(&self) -> Span {
321        self.macro_span
322    }
323
324    /// The span of the macro stream (the argument).
325    ///
326    /// If the macro call was `stringify!(a + b)` this would refer to `a + b`.
327    pub fn input_span(&self) -> Span {
328        self.input_span
329    }
330}
331
332pub struct Stringify<'cx, 'a, 'b, 'arena> {
333    cx: &'cx MacroContext<'a, 'b, 'arena>,
334    stream: TokenStream,
335}
336
337impl fmt::Display for Stringify<'_, '_, '_, '_> {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        let mut it = self.stream.iter();
340        let last = it.next_back();
341
342        for token in it {
343            token.token_fmt(self.cx, f)?;
344            write!(f, " ")?;
345        }
346
347        if let Some(last) = last {
348            last.token_fmt(self.cx, f)?;
349        }
350
351        Ok(())
352    }
353}