use crate as rune;
use crate::ast;
use crate::compile;
use crate::macros::{quote, MacroContext, ToTokens, TokenStream};
use crate::parse::Parser;
use crate::termcolor::{ColorChoice, StandardStream};
use crate::{Context, Diagnostics, Module, Vm};
use crate::tests::prelude::*;
mod stringy_math;
#[test]
fn test_concat_idents() -> rune::support::Result<()> {
let sources = rune::sources! {
entry => {
use ::test::macros::concat_idents;
fn function() {
42
}
pub fn main() {
let foobar = concat_idents!(fu, nction)();
concat_idents!(foo, bar)
}
}
};
let mut vm = compile(sources)?;
let value = vm.call(["main"], ())?;
let value: u32 = rune::from_value(value)?;
assert_eq!(value, 42);
Ok(())
}
#[test]
fn test_rename() -> rune::support::Result<()> {
let mut vm = compile(rune::sources! {
entry => {
use ::test::macros::rename;
#[rename = foobar]
fn renamed() {
42
}
pub fn main() {
let foobar = foobar();
foobar
}
}
})?;
let value = vm.call(["main"], ())?;
let value: u32 = rune::from_value(value)?;
assert_eq!(value, 42);
Ok(())
}
#[test]
fn test_make_function() -> rune::support::Result<()> {
let mut vm = compile(rune::sources! {
entry => {
pub fn main() {
root_fn()
}
make_function!(root_fn => { 42 });
use ::test::macros::make_function;
}
})?;
let value = vm.call(["main"], ())?;
let value: u32 = rune::from_value(value)?;
assert_eq!(value, 42);
Ok(())
}
#[test]
fn test_stringy_math() -> rune::support::Result<()> {
let mut vm = compile(rune::sources! {
entry => {
use ::test::macros::stringy_math;
pub fn main() {
stringy_math!(add 10 sub 2 div 3 mul 100)
}
}
})?;
let value = vm.call(["main"], ())?;
let value: u32 = rune::from_value(value)?;
assert_eq!(value, 200);
Ok(())
}
#[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.push_str(cx.resolve(ident)?);
while p.parse::<Option<T![,]>>()?.is_some() {
if p.is_eof()? {
break;
}
let ident = p.parse::<ast::Ident>()?;
output.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)
}
#[rune::macro_]
fn passthrough(
_: &mut MacroContext<'_, '_, '_>,
stream: &TokenStream,
) -> compile::Result<TokenStream> {
Ok(stream.try_clone()?)
}
#[rune::macro_]
fn make_function(
cx: &mut MacroContext<'_, '_, '_>,
stream: &TokenStream,
) -> compile::Result<TokenStream> {
let mut parser = Parser::from_token_stream(stream, cx.input_span());
let ident = parser.parse::<ast::Ident>()?;
let _ = parser.parse::<T![=>]>()?;
let output = parser.parse::<ast::ExprBlock>()?;
parser.eof()?;
Ok(quote!(fn #ident() { #output }).into_token_stream(cx)?)
}
fn compile(mut sources: Sources) -> Result<Vm> {
let mut m = Module::with_crate_item("test", ["macros"])?;
m.macro_meta(concat_idents)?;
m.macro_meta(rename)?;
m.macro_meta(stringy_math::stringy_math)?;
m.macro_meta(passthrough)?;
m.macro_meta(make_function)?;
let mut context = Context::with_default_modules()?;
context.install(m)?;
let runtime = Arc::new(context.runtime()?);
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::new(unit);
Ok(Vm::new(runtime, unit))
}