Skip to main content

Module macros

Module macros 

Source
Expand description

The macro system of Rune.

Macros are registered with Module::macro_ and are function-like items that are expanded at compile time.

Macros take token streams as arguments and are responsible for translating them into another token stream that will be embedded into the source location where the macro was invoked.

The attribute macros rune::macro_ for function macros (some_macro!( ... )) and rune::attribute_macro for attribute macros (#[some_macro ...]).

There are two ways to make sense of an input. A macro which needs to know what an argument is parses it into a syntax tree with MacroContext::parser, which is bounded by the max-ast-depth option since a tree is walked by recursing over it. A macro which only needs to know where each argument ends - which is what the standard library’s own macros need - splits the input with MacroContext::exprs and passes each argument on as the tokens it was written as, which neither recurses nor holds the input to that much smaller bound.

use rune::{T, Context, Diagnostics, Module, Vm};
use rune::ast;
use rune::compile;
use rune::macros::{quote, MacroContext, TokenStream, ToTokens};
use rune::parse::Parser;
use rune::termcolor::{ColorChoice, StandardStream};
use rune::alloc::String;
use rune::sync::Arc;

#[rune::macro_]
fn concat_idents(cx: &mut MacroContext<'_, '_, '_>, input: &TokenStream) -> compile::Result<TokenStream> {
    let mut output = String::new();

    let mut p = Parser::from_token_stream(input, cx.input_span());

    let ident = p.parse::<ast::Ident>()?;
    output.try_push_str(cx.resolve(ident)?)?;

    while p.parse::<Option<T![,]>>()?.is_some() {
        if p.is_eof()? {
            break;
        }

        let ident = p.parse::<ast::Ident>()?;
        output.try_push_str(cx.resolve(ident)?)?;
    }

    p.eof()?;

    let output = cx.ident(&output)?;
    Ok(quote!(#output).into_token_stream(cx)?)
}

#[rune::attribute_macro]
fn rename(cx: &mut MacroContext<'_, '_, '_>, input: &TokenStream, item: &TokenStream) -> compile::Result<TokenStream> {
    let mut parser = Parser::from_token_stream(item, cx.macro_span());
    let mut fun: ast::ItemFn = parser.parse_all()?;

    let mut parser = Parser::from_token_stream(input, cx.input_span());
    fun.name = parser.parse_all::<ast::EqValue<_>>()?.value;

    let mut tokens = TokenStream::new();
    fun.to_tokens(cx, &mut tokens);
    Ok(tokens)
}

let mut m = Module::new();
m.macro_meta(concat_idents)?;
m.macro_meta(rename)?;

let mut context = Context::new();
context.install(m)?;

let runtime = Arc::try_new(context.runtime()?)?;

let mut sources = rune::sources! {
    entry => {
        #[rename = foobar]
        fn renamed() {
            42
        }

        pub fn main() {
            let foobar = foobar();
            concat_idents!(foo, bar)
        }
    }
};

let mut diagnostics = Diagnostics::new();

let result = rune::prepare(&mut sources)
    .with_context(&context)
    .with_diagnostics(&mut diagnostics)
    .build();

if !diagnostics.is_empty() {
    let mut writer = StandardStream::stderr(ColorChoice::Always);
    diagnostics.emit(&mut writer, &sources)?;
}

let unit = result?;
let unit = Arc::try_new(unit)?;

let mut vm = Vm::new(runtime, unit);
let value = vm.call(["main"], ())?;
let value: u32 = rune::from_value(value)?;

assert_eq!(value, 42);

Structs§

FormatArgs
A format specification: A format string followed by arguments to be formatted in accordance with that string.
MacroContext
Context for a running macro.
Quote
ToTokens implementation generated by quote_fn.
SyntheticId
A synthetic identifier which can be used to reference something in storage.
TokenStream
A token stream.
TokenStreamIter
A token stream iterator.

Enums§

SyntheticKind
The kind of a synthetic token.

Traits§

IntoLit
Helper trait used for things that can be converted into tokens.
ToTokens
Trait for things that can be turned into tokens.

Functions§

quote_fn
Construct a token stream from a function.
teststd
Construct an empty macro context which can be used for testing.