diff --git a/crates/rune-macros/src/lib.rs b/crates/rune-macros/src/lib.rs index f08a5c2c7..90f627a08 100644 --- a/crates/rune-macros/src/lib.rs +++ b/crates/rune-macros/src/lib.rs @@ -22,6 +22,7 @@ //! //! This is part of the [Rune Language](https://rune-rs.github.io). +use ::quote::format_ident; use syn::{Generics, Path}; extern crate proc_macro; @@ -79,7 +80,23 @@ pub fn macro_( let attrs = syn::parse_macro_input!(attrs with crate::macro_::Config::parse); let macro_ = syn::parse_macro_input!(item with crate::macro_::Macro::parse); - let output = match macro_.expand(attrs) { + let output = match macro_.expand(attrs, format_ident!("function")) { + Ok(output) => output, + Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()), + }; + + output.into() +} + +#[proc_macro_attribute] +pub fn attribute_macro( + attrs: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let attrs = syn::parse_macro_input!(attrs with crate::macro_::Config::parse); + let macro_ = syn::parse_macro_input!(item with crate::macro_::Macro::parse); + + let output = match macro_.expand(attrs, format_ident!("attribute")) { Ok(output) => output, Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()), }; diff --git a/crates/rune-macros/src/macro_.rs b/crates/rune-macros/src/macro_.rs index 0c842b795..83eff340f 100644 --- a/crates/rune-macros/src/macro_.rs +++ b/crates/rune-macros/src/macro_.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Span, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; use syn::parse::ParseStream; use syn::punctuated::Punctuated; @@ -109,7 +109,7 @@ impl Macro { } /// Expand the function declaration. - pub(crate) fn expand(self, attrs: Config) -> Result { + pub(crate) fn expand(self, attrs: Config, macro_kind: Ident) -> Result { let real_fn_path = { let mut segments = Punctuated::default(); @@ -181,7 +181,7 @@ impl Macro { #[automatically_derived] #meta_vis fn #meta_fn() -> rune::__private::MacroMetaData { rune::__private::MacroMetaData { - kind: rune::__private::MacroMetaKind::function(#meta_name, #real_fn_path), + kind: rune::__private::MacroMetaKind::#macro_kind(#meta_name, #real_fn_path), name: #name_string, docs: &#docs[..], } diff --git a/crates/rune-modules/src/experiments.rs b/crates/rune-modules/src/experiments.rs index 0fdec55ad..3f0f7958f 100644 --- a/crates/rune-modules/src/experiments.rs +++ b/crates/rune-modules/src/experiments.rs @@ -45,7 +45,7 @@ fn passthrough(_: &mut MacroContext<'_>, stream: &TokenStream) -> compile::Resul /// Implementation for the `make_function!` macro. #[rune::macro_] fn make_function(ctx: &mut MacroContext<'_>, stream: &TokenStream) -> compile::Result { - let mut parser = Parser::from_token_stream(stream, ctx.stream_span()); + let mut parser = Parser::from_token_stream(stream, ctx.input_span()); let ident = parser.parse::()?; let _ = parser.parse::]>()?; diff --git a/crates/rune-modules/src/experiments/stringy_math_macro.rs b/crates/rune-modules/src/experiments/stringy_math_macro.rs index 905439577..e39a02b20 100644 --- a/crates/rune-modules/src/experiments/stringy_math_macro.rs +++ b/crates/rune-modules/src/experiments/stringy_math_macro.rs @@ -9,7 +9,7 @@ pub(crate) fn stringy_math( ctx: &mut MacroContext<'_>, stream: &TokenStream, ) -> compile::Result { - let mut parser = Parser::from_token_stream(stream, ctx.stream_span()); + let mut parser = Parser::from_token_stream(stream, ctx.input_span()); let mut output = quote!(0); diff --git a/crates/rune/src/ast.rs b/crates/rune/src/ast.rs index e60004276..b9981a442 100644 --- a/crates/rune/src/ast.rs +++ b/crates/rune/src/ast.rs @@ -16,7 +16,7 @@ //! //! #[rune::macro_] //! fn ident_to_string(ctx: &mut MacroContext<'_>, stream: &TokenStream) -> compile::Result { -//! let mut p = Parser::from_token_stream(stream, ctx.stream_span()); +//! let mut p = Parser::from_token_stream(stream, ctx.input_span()); //! let ident = p.parse_all::()?; //! let ident = ctx.resolve(ident)?.to_owned(); //! let string = ctx.lit(&ident); @@ -148,6 +148,7 @@ mod lit_number; mod lit_str; mod local; mod macro_call; +mod macro_utils; mod pat; mod path; mod prelude; @@ -213,6 +214,7 @@ pub use self::lit_number::LitNumber; pub use self::lit_str::LitStr; pub use self::local::Local; pub use self::macro_call::MacroCall; +pub use self::macro_utils::{EqValue, Group}; pub use self::pat::{ Pat, PatBinding, PatIgnore, PatLit, PatObject, PatPath, PatRest, PatTuple, PatVec, }; diff --git a/crates/rune/src/ast/attribute.rs b/crates/rune/src/ast/attribute.rs index 877a23042..ca6415231 100644 --- a/crates/rune/src/ast/attribute.rs +++ b/crates/rune/src/ast/attribute.rs @@ -60,6 +60,13 @@ pub struct Attribute { /// The `]` character pub close: T![']'], } +impl Attribute { + pub(crate) fn input_span(&self) -> Span { + self.input + .option_span() + .unwrap_or_else(|| self.close.span.head()) + } +} impl Parse for Attribute { fn parse(p: &mut Parser<'_>) -> Result { diff --git a/crates/rune/src/ast/item.rs b/crates/rune/src/ast/item.rs index 5c7b0f128..3c232e0d6 100644 --- a/crates/rune/src/ast/item.rs +++ b/crates/rune/src/ast/item.rs @@ -2,6 +2,8 @@ use core::mem::take; use crate::ast::prelude::*; +use super::Attribute; + /// A declaration. #[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)] #[non_exhaustive] @@ -26,7 +28,7 @@ pub enum Item { } impl Item { - /// Test if the item has any attributes + /// Get the item's attributes pub(crate) fn attributes(&self) -> &[ast::Attribute] { match self { Self::Use(item) => &item.attributes, @@ -39,6 +41,19 @@ impl Item { Self::MacroCall(item) => &item.attributes, } } + /// Get the item's attributes mutably + pub(crate) fn attributes_mut(&mut self) -> &mut Vec { + match self { + Self::Use(item) => &mut item.attributes, + Self::Fn(item) => &mut item.attributes, + Self::Enum(item) => &mut item.attributes, + Self::Struct(item) => &mut item.attributes, + Self::Impl(item) => &mut item.attributes, + Self::Mod(item) => &mut item.attributes, + Self::Const(item) => &mut item.attributes, + Self::MacroCall(item) => &mut item.attributes, + } + } /// Indicates if the declaration needs a semi-colon or not. pub(crate) fn needs_semi_colon(&self) -> bool { @@ -155,6 +170,17 @@ impl Item { Ok(item) } + + /// Removes the first attribute in the item list and returns it if present. + pub(crate) fn remove_first_attribute(&mut self) -> Option { + let attributes = self.attributes_mut(); + + if !attributes.is_empty() { + return Some(attributes.remove(0)); + } + + None + } } impl Parse for Item { diff --git a/crates/rune/src/ast/macro_call.rs b/crates/rune/src/ast/macro_call.rs index efdd5e0cc..9781709e2 100644 --- a/crates/rune/src/ast/macro_call.rs +++ b/crates/rune/src/ast/macro_call.rs @@ -29,7 +29,7 @@ pub struct MacroCall { pub open: ast::Token, /// The tokens provided to the macro. #[rune(optional)] - pub stream: TokenStream, + pub input: TokenStream, /// Closing token. pub close: ast::Token, } @@ -40,9 +40,9 @@ impl MacroCall { !matches!(self.close.kind, K!['}']) } - /// The span of the token stream. - pub(crate) fn stream_span(&self) -> Span { - if let Some(span) = self.stream.option_span() { + /// The span of the input token stream. + pub(crate) fn input_span(&self) -> Span { + if let Some(span) = self.input.option_span() { span } else { self.open.span.tail() @@ -106,7 +106,7 @@ impl MacroCall { bang, path, open, - stream: TokenStream::from(stream), + input: TokenStream::from(stream), close, }) } diff --git a/crates/rune/src/ast/macro_utils.rs b/crates/rune/src/ast/macro_utils.rs new file mode 100644 index 000000000..8d9e23849 --- /dev/null +++ b/crates/rune/src/ast/macro_utils.rs @@ -0,0 +1,77 @@ +use super::prelude::*; +use super::Eq; + +/// An `= ...` e.g. inside an attribute `#[doc = ...]`. +/// +/// To get unparsed tokens use `EqValue`. +#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Parse, Spanned)] +pub struct EqValue { + /// The `=` token. + pub eq: Eq, + /// The remainder. + pub value: T, +} + +/// Parses `[{( ... )}]` ensuring that the delimiter is balanced. +#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)] +pub struct Group { + /// The opening delimiter. + pub open: ast::Token, + /// The content between the delimiters. + pub content: TokenStream, + /// The closing delimiter. + pub close: ast::Token, +} + +impl Parse for Group { + fn parse(parser: &mut Parser) -> compile::Result { + let mut level = 1; + let open = parser.next()?; + + let delim = match open.kind { + ast::Kind::Open(delim) => delim, + _ => { + return Err(compile::Error::expected(open, Expectation::OpenDelimiter)); + } + }; + + let close; + + let mut stream = Vec::new(); + + loop { + let token = parser.next()?; + + match token.kind { + ast::Kind::Open(..) => level += 1, + ast::Kind::Close(actual) => { + level -= 1; + + if level == 0 { + if actual != delim { + return Err(compile::Error::new( + open, + ParseErrorKind::ExpectedMacroCloseDelimiter { + actual: token.kind, + expected: ast::Kind::Close(delim), + }, + )); + } + + close = token; + break; + } + } + _ => (), + } + + stream.push(token); + } + + Ok(Self { + open, + content: TokenStream::from(stream), + close, + }) + } +} diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index a87e233bc..8b0b497df 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -9,12 +9,12 @@ use crate::compile::meta; use crate::compile::Docs; use crate::compile::{ComponentRef, ContextError, IntoComponent, Item, ItemBuf, MetaInfo, Names}; use crate::module::{ - Fields, Function, InternalEnum, Module, ModuleAssociated, ModuleConstant, ModuleFunction, - ModuleMacro, ModuleType, TypeSpecification, UnitType, + Fields, Function, InternalEnum, Module, ModuleAssociated, ModuleAttributeMacro, ModuleConstant, + ModuleFunction, ModuleMacro, ModuleType, TypeSpecification, UnitType, }; use crate::runtime::{ - ConstValue, FunctionHandler, MacroHandler, Protocol, RuntimeContext, StaticType, TypeCheck, - TypeInfo, TypeOf, VariantRtti, + AttributeMacroHandler, ConstValue, FunctionHandler, MacroHandler, Protocol, RuntimeContext, + StaticType, TypeCheck, TypeInfo, TypeOf, VariantRtti, }; use crate::Hash; @@ -92,6 +92,8 @@ pub struct Context { associated: HashMap>, /// Registered native macro handlers. macros: HashMap>, + /// Registered native attribute macro handlers. + attribute_macros: HashMap>, /// Registered types. types: HashMap, /// Registered internal enums. @@ -217,6 +219,10 @@ impl Context { self.install_macro(module, m)?; } + for m in &module.attribute_macros { + self.install_attribute_macro(module, m)?; + } + for m in &module.constants { self.install_constant(module, m)?; } @@ -331,6 +337,11 @@ impl Context { self.macros.get(&hash) } + /// Lookup the given attribute macro handler. + pub(crate) fn lookup_attribute_macro(&self, hash: Hash) -> Option<&Arc> { + self.attribute_macros.get(&hash) + } + /// Look up the type check implementation for the specified type hash. pub(crate) fn type_check_for(&self, hash: Hash) -> Option { let ty = self.types.get(&hash)?; @@ -593,7 +604,7 @@ impl Context { Ok(()) } - /// Install a function and check for duplicates. + /// Install a macro and check for duplicates. fn install_macro(&mut self, module: &Module, m: &ModuleMacro) -> Result<(), ContextError> { let item = module.item.join(&m.item); let hash = Hash::type_hash(&item); @@ -610,6 +621,27 @@ impl Context { Ok(()) } + /// Install an attribute macro and check for duplicates. + fn install_attribute_macro( + &mut self, + module: &Module, + m: &ModuleAttributeMacro, + ) -> Result<(), ContextError> { + let item = module.item.join(&m.item); + let hash = Hash::type_hash(&item); + self.attribute_macros.insert(hash, m.handler.clone()); + + self.install_meta(ContextMeta { + hash, + item: Some(item), + kind: meta::Kind::AttributeMacro, + #[cfg(feature = "doc")] + docs: m.docs.clone(), + })?; + + Ok(()) + } + /// Install a constant and check for duplicates. fn install_constant( &mut self, diff --git a/crates/rune/src/compile/meta.rs b/crates/rune/src/compile/meta.rs index 2fc889af1..e1f741e9b 100644 --- a/crates/rune/src/compile/meta.rs +++ b/crates/rune/src/compile/meta.rs @@ -120,6 +120,7 @@ impl Meta { Kind::ConstFn { .. } => None, Kind::Import { .. } => None, Kind::Macro => None, + Kind::AttributeMacro => None, Kind::Module => None, } } @@ -173,6 +174,8 @@ pub enum Kind { }, /// A macro item. Macro, + /// An attribute macro item. + AttributeMacro, /// A function declaration. Function { /// Native signature for this function. diff --git a/crates/rune/src/compile/meta_info.rs b/crates/rune/src/compile/meta_info.rs index de5cb024c..118f6e153 100644 --- a/crates/rune/src/compile/meta_info.rs +++ b/crates/rune/src/compile/meta_info.rs @@ -60,6 +60,9 @@ impl fmt::Display for MetaInfo { MetaInfoKind::Macro => { write!(fmt, "macro {name}")?; } + MetaInfoKind::AttributeMacro => { + write!(fmt, "attribute macro {name}")?; + } MetaInfoKind::Function => { write!(fmt, "fn {name}")?; } @@ -97,6 +100,7 @@ pub(crate) enum MetaInfoKind { Variant, Enum, Macro, + AttributeMacro, Function, Associated, Closure, @@ -115,6 +119,7 @@ impl MetaInfoKind { meta::Kind::Variant { .. } => MetaInfoKind::Variant, meta::Kind::Enum { .. } => MetaInfoKind::Enum, meta::Kind::Macro { .. } => MetaInfoKind::Macro, + meta::Kind::AttributeMacro { .. } => MetaInfoKind::AttributeMacro, meta::Kind::Function { .. } => MetaInfoKind::Function, meta::Kind::AssociatedFunction { .. } => MetaInfoKind::Associated, meta::Kind::Closure { .. } => MetaInfoKind::Closure, diff --git a/crates/rune/src/compile/unit_builder.rs b/crates/rune/src/compile/unit_builder.rs index a009b159e..1d36e52b5 100644 --- a/crates/rune/src/compile/unit_builder.rs +++ b/crates/rune/src/compile/unit_builder.rs @@ -504,6 +504,7 @@ impl UnitBuilder { ); } meta::Kind::Macro { .. } => (), + meta::Kind::AttributeMacro { .. } => (), meta::Kind::Function { .. } => (), meta::Kind::AssociatedFunction { .. } => (), meta::Kind::Closure { .. } => (), diff --git a/crates/rune/src/fmt/printer.rs b/crates/rune/src/fmt/printer.rs index 771ea5ec3..9425a0f99 100644 --- a/crates/rune/src/fmt/printer.rs +++ b/crates/rune/src/fmt/printer.rs @@ -658,7 +658,7 @@ impl<'a> Printer<'a> { path, bang, open, - stream: _, + input: _, close, } = macrocall; diff --git a/crates/rune/src/indexing/index.rs b/crates/rune/src/indexing/index.rs index 98c985c33..6a575aa35 100644 --- a/crates/rune/src/indexing/index.rs +++ b/crates/rune/src/indexing/index.rs @@ -7,8 +7,7 @@ use crate::no_std::path::PathBuf; use crate::no_std::prelude::*; use crate::no_std::sync::Arc; -use crate::ast::{self}; -use crate::ast::{OptionSpanned, Span, Spanned}; +use crate::ast::{self, OptionSpanned, Span, Spanned}; use crate::compile::attrs::Attributes; use crate::compile::{ self, attrs, ir, CompileErrorKind, Doc, ItemId, Location, ModId, Options, ParseErrorKind, @@ -181,7 +180,7 @@ impl<'a> Indexer<'a> { ast: &mut ast::MacroCall, args: &attrs::BuiltInArgs, ) -> compile::Result { - let mut p = Parser::from_token_stream(&ast.stream, ast.span()); + let mut p = Parser::from_token_stream(&ast.input, ast.span()); let mut exprs = Vec::new(); while !p.is_eof()? { @@ -207,7 +206,7 @@ impl<'a> Indexer<'a> { ast: &mut ast::MacroCall, _: &attrs::BuiltInArgs, ) -> compile::Result { - let mut p = Parser::from_token_stream(&ast.stream, ast.span()); + let mut p = Parser::from_token_stream(&ast.input, ast.span()); let value = p.parse::()?; @@ -424,6 +423,35 @@ impl<'a> Indexer<'a> { Ok(expanded) } + /// Perform an attribute macro expansion. + fn expand_attribute_macro( + &mut self, + attr: &mut ast::Attribute, + item: &mut ast::Item, + ) -> compile::Result> + where + T: Parse, + { + let id = self + .q + .insert_path(self.mod_item, self.impl_item, &self.items.item()); + attr.path.id.set(id); + let id = self.items.id().with_span(attr.span())?; + + let containing = self.q.item_for((attr.span(), id))?; + + let mut compiler = MacroCompiler { + item_meta: containing, + options: self.options, + context: self.context, + query: self.q.borrow(), + }; + + let expanded = compiler.eval_attribute_macro::(attr, item)?; + self.q.remove_path_by_id(attr.path.id); + Ok(expanded) + } + /// Construct the calling convention based on the parameters. fn call(generator: bool, kind: IndexFnKind) -> Option { match kind { @@ -525,14 +553,18 @@ pub(crate) fn file(ast: &mut ast::File, idx: &mut Indexer<'_>) -> compile::Resul // Items take priority. let mut head = VecDeque::new(); - // Macros are expanded as they are encountered, but after regular items have + + // Macros and items with attributes are expanded as they are encountered, but after regular items have // been processed. let mut queue = VecDeque::new(); for (item, semi) in ast.items.drain(..) { match item { - ast::Item::MacroCall(macro_call) => { - queue.push_back((0, macro_call, semi)); + i @ ast::Item::MacroCall(_) => { + queue.push_back((0, i, Vec::new(), semi)); + } + i if !i.attributes().is_empty() => { + queue.push_back((0, i, Vec::new(), semi)); } i => { head.push_back((i, semi)); @@ -552,10 +584,10 @@ pub(crate) fn file(ast: &mut ast::File, idx: &mut Indexer<'_>) -> compile::Resul item(i, idx)?; } - while let Some((depth, mut macro_call, semi)) = queue.pop_front() { + while let Some((depth, mut item, mut skipped_attributes, semi)) = queue.pop_front() { if depth >= MAX_MACRO_RECURSION { return Err(compile::Error::new( - macro_call.span(), + item.span(), CompileErrorKind::MaxMacroRecursion { depth, max: MAX_MACRO_RECURSION, @@ -563,7 +595,61 @@ pub(crate) fn file(ast: &mut ast::File, idx: &mut Indexer<'_>) -> compile::Resul )); } - let mut attributes = attrs::Attributes::new(macro_call.attributes.to_vec()); + // Before furthor processing all attributes are either expanded, or if unknown put in + // `skipped_attributes`, to either be reinserted for the `item` handler or to be used + // by the macro_call expansion below. + if let Some(mut attr) = item.remove_first_attribute() { + match idx.expand_attribute_macro::(&mut attr, &mut item)? { + Some(file) => { + for (item, semi) in file.items.into_iter().rev() { + match item { + item @ ast::Item::MacroCall(_) => { + queue.push_back(( + depth.wrapping_add(1), + item, + Vec::new(), + semi, + )); + } + item if !item.attributes().is_empty() => { + queue.push_back(( + depth.wrapping_add(1), + item, + Vec::new(), + semi, + )); + } + item => { + head.push_front((item, semi)); + } + } + } + } + None => { + skipped_attributes.push(attr); + if !matches!(item, ast::Item::MacroCall(_)) && item.attributes().is_empty() + { + // For all we know only non macro attributes remain, which will be + // handled by the item handler. + *item.attributes_mut() = skipped_attributes; + head.push_front((item, semi)); + } else { + // items with remaining attributes and macro calls will be dealt with by + // reinserting in the queue. + queue.push_back((depth, item, skipped_attributes, semi)) + } + } + }; + continue; + } + + let ast::Item::MacroCall(mut macro_call) = item else { + unreachable!("non macro items would have had attributes") + }; + + macro_call.attributes = skipped_attributes.clone(); + + let mut attributes = attrs::Attributes::new(skipped_attributes); if idx.try_expand_internal_macro(&mut attributes, &mut macro_call)? { // Macro call must be added to output to make sure its instructions are assembled. @@ -573,11 +659,14 @@ pub(crate) fn file(ast: &mut ast::File, idx: &mut Indexer<'_>) -> compile::Resul for (item, semi) in file.items.into_iter().rev() { match item { - ast::Item::MacroCall(macro_call) => { - queue.push_front((depth.wrapping_add(1), macro_call, semi)); + item @ ast::Item::MacroCall(_) => { + queue.push_back((depth.wrapping_add(1), item, Vec::new(), semi)); + } + item if !item.attributes().is_empty() => { + queue.push_back((depth.wrapping_add(1), item, Vec::new(), semi)); } - i => { - head.push_front((i, semi)); + item => { + head.push_front((item, semi)); } } } diff --git a/crates/rune/src/lib.rs b/crates/rune/src/lib.rs index 42170eada..244c567aa 100644 --- a/crates/rune/src/lib.rs +++ b/crates/rune/src/lib.rs @@ -388,6 +388,13 @@ pub use rune_macros::function; #[doc(hidden)] pub use rune_macros::macro_; +/// Macro used to annotate native functions which can be loaded as attribute macros in +/// rune. +/// +/// See [`Module::macro_meta`]. +#[doc(hidden)] +pub use rune_macros::attribute_macro; + cfg_cli! { pub mod cli; } diff --git a/crates/rune/src/macros.rs b/crates/rune/src/macros.rs index 886720581..83deb8f5b 100644 --- a/crates/rune/src/macros.rs +++ b/crates/rune/src/macros.rs @@ -3,23 +3,26 @@ //! Macros are registered with [Module::macro_][crate::Module::macro_] and are //! function-like items that are expanded at compile time. //! -//! Macros take a token stream as an argument and is responsible for translating -//! it into another token stream that will be embedded into the source location +//! 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_`](crate::macro_) for function macros (`some_macro!( ... )`) and +//! [`rune::attribute_macro`](crate::attribute_macro) for attribute macros (`#[some_macro ...]`). +//! //! ``` //! use rune::{T, Context, Module, Vm}; //! use rune::ast; //! use rune::compile; -//! use rune::macros::{quote, MacroContext, TokenStream}; +//! use rune::macros::{quote, MacroContext, TokenStream, ToTokens}; //! use rune::parse::Parser; //! use std::sync::Arc; //! //! #[rune::macro_] -//! fn concat_idents(ctx: &mut MacroContext<'_>, stream: &TokenStream) -> compile::Result { +//! fn concat_idents(ctx: &mut MacroContext<'_>, input: &TokenStream) -> compile::Result { //! let mut output = String::new(); //! -//! let mut p = Parser::from_token_stream(stream, ctx.stream_span()); +//! let mut p = Parser::from_token_stream(input, ctx.input_span()); //! //! let ident = p.parse::()?; //! output.push_str(ctx.resolve(ident)?); @@ -39,9 +42,23 @@ //! Ok(quote!(#output).into_token_stream(ctx)) //! } //! +//! #[rune::attribute_macro] +//! fn rename(ctx: &mut MacroContext<'_>, input: &TokenStream, item: &TokenStream) -> compile::Result { +//! let mut parser = Parser::from_token_stream(item, ctx.macro_span()); +//! let mut fun: ast::ItemFn = parser.parse_all()?; +//! +//! let mut parser = Parser::from_token_stream(input, ctx.input_span()); +//! fun.name = parser.parse_all::>()?.value; +//! +//! let mut tokens = TokenStream::new(); +//! fun.to_tokens(ctx, &mut tokens); +//! Ok(tokens) +//! } +//! //! # fn main() -> rune::Result<()> { //! let mut m = Module::new(); //! m.macro_meta(concat_idents)?; +//! m.macro_meta(rename)?; //! //! let mut context = Context::new(); //! context.install(m)?; @@ -50,8 +67,13 @@ //! //! let mut sources = rune::sources! { //! entry => { +//! #[rename = foobar] +//! fn renamed() { +//! 42 +//! } +//! //! pub fn main() { -//! let foobar = 42; +//! let foobar = foobar(); //! concat_idents!(foo, bar) //! } //! } diff --git a/crates/rune/src/macros/macro_compiler.rs b/crates/rune/src/macros/macro_compiler.rs index 318414130..75a5ce01d 100644 --- a/crates/rune/src/macros/macro_compiler.rs +++ b/crates/rune/src/macros/macro_compiler.rs @@ -5,11 +5,13 @@ use crate::no_std::prelude::*; use crate::ast; use crate::ast::Spanned; use crate::compile::{self, CompileErrorKind, ItemMeta, Options}; -use crate::macros::MacroContext; +use crate::macros::{MacroContext, ToTokens}; use crate::parse::{Parse, Parser}; use crate::query::Query; use crate::Context; +use super::TokenStream; + pub(crate) struct MacroCompiler<'a> { pub(crate) item_meta: ItemMeta, pub(crate) options: &'a Options, @@ -55,12 +57,12 @@ impl MacroCompiler<'_> { } }; - let input_stream = ¯o_call.stream; + let input_stream = ¯o_call.input; let token_stream = { let mut macro_context = MacroContext { macro_span: macro_call.span(), - stream_span: macro_call.stream_span(), + input_span: macro_call.input_span(), item_meta: self.item_meta, q: self.query.borrow(), }; @@ -74,4 +76,58 @@ impl MacroCompiler<'_> { Ok(output) } + + /// Compile the given macro into the given output type. + pub(crate) fn eval_attribute_macro( + &mut self, + attribute: &ast::Attribute, + item: &ast::Item, + ) -> compile::Result> + where + T: Parse, + { + let span = attribute.span(); + + if !self.options.macros { + return Ok(None); + } + + // TODO: include information on the module the macro is being called + // from. + // + // TODO: Figure out how to avoid performing ad-hoc lowering here. + let arena = crate::hir::Arena::new(); + let ctx = crate::hir::lowering::Ctx::new(&arena, self.query.borrow()); + let path = crate::hir::lowering::path(&ctx, &attribute.path)?; + let named = self.query.convert_path(self.context, &path)?; + + let hash = self.query.pool.item_type_hash(named.item); + + let handler = match self.context.lookup_attribute_macro(hash) { + Some(handler) => handler, + None => { + return Ok(None); + } + }; + + let input_stream = &attribute.input; + + let token_stream = { + let mut macro_context = MacroContext { + macro_span: attribute.span(), + input_span: attribute.input_span(), + item_meta: self.item_meta, + q: self.query.borrow(), + }; + + let mut item_stream = TokenStream::new(); + item.to_tokens(&mut macro_context, &mut item_stream); + + handler(&mut macro_context, input_stream, &item_stream)? + }; + + let mut parser = Parser::from_token_stream(&token_stream, span); + + parser.parse_all().map(Some) + } } diff --git a/crates/rune/src/macros/macro_context.rs b/crates/rune/src/macros/macro_context.rs index 9fa0e07dd..5e73034dd 100644 --- a/crates/rune/src/macros/macro_context.rs +++ b/crates/rune/src/macros/macro_context.rs @@ -18,8 +18,8 @@ use crate::{Source, SourceId, Sources}; pub struct MacroContext<'a> { /// Macro span of the full macro call. pub(crate) macro_span: Span, - /// Macro span of the stream. - pub(crate) stream_span: Span, + /// Macro span of the input. + pub(crate) input_span: Span, /// The item where the macro is being evaluated. pub(crate) item_meta: ItemMeta, /// Accessible query required to run the IR interpreter and has access to @@ -65,7 +65,7 @@ impl<'a> MacroContext<'a> { let mut ctx = MacroContext { macro_span: Span::empty(), - stream_span: Span::empty(), + input_span: Span::empty(), item_meta: Default::default(), q: query.borrow(), }; @@ -90,7 +90,7 @@ impl<'a> MacroContext<'a> { /// MacroContext::test(|ctx| { /// let stream = quote!(1 + 2).into_token_stream(ctx); /// - /// let mut p = Parser::from_token_stream(&stream, ctx.stream_span()); + /// let mut p = Parser::from_token_stream(&stream, ctx.input_span()); /// let expr = p.parse_all::().unwrap(); /// let value = ctx.eval(&expr).unwrap(); /// @@ -238,8 +238,8 @@ impl<'a> MacroContext<'a> { /// The span of the macro stream (the argument). /// /// If the macro call was `stringify!(a + b)` this would refer to `a + b`. - pub fn stream_span(&self) -> Span { - self.stream_span + pub fn input_span(&self) -> Span { + self.input_span } } diff --git a/crates/rune/src/module.rs b/crates/rune/src/module.rs index c1695b02b..8e76d9efc 100644 --- a/crates/rune/src/module.rs +++ b/crates/rune/src/module.rs @@ -15,7 +15,8 @@ use crate::no_std::sync::Arc; use crate::compile::{meta, ContextError, Docs, IntoComponent, Item, ItemBuf}; use crate::runtime::{ - ConstValue, FullTypeOf, FunctionHandler, MacroHandler, StaticType, TypeCheck, TypeInfo, TypeOf, + AttributeMacroHandler, ConstValue, FullTypeOf, FunctionHandler, MacroHandler, StaticType, + TypeCheck, TypeInfo, TypeOf, }; use crate::Hash; @@ -229,6 +230,13 @@ pub(crate) struct ModuleMacro { pub(crate) docs: Docs, } +/// Handle to an attribute macro inserted into a module. +pub(crate) struct ModuleAttributeMacro { + pub(crate) item: ItemBuf, + pub(crate) handler: Arc, + pub(crate) docs: Docs, +} + /// A constant registered in a module. pub(crate) struct ModuleConstant { pub(crate) item: ItemBuf, diff --git a/crates/rune/src/module/function_meta.rs b/crates/rune/src/module/function_meta.rs index 25b8501f6..a01b9bb73 100644 --- a/crates/rune/src/module/function_meta.rs +++ b/crates/rune/src/module/function_meta.rs @@ -9,7 +9,8 @@ use crate::hash::Hash; use crate::macros::{MacroContext, TokenStream}; use crate::module::{AssociatedKey, Function, FunctionKind, InstanceFunction}; use crate::runtime::{ - FullTypeOf, FunctionHandler, MacroHandler, MaybeTypeOf, Protocol, TypeInfo, TypeOf, + AttributeMacroHandler, FullTypeOf, FunctionHandler, MacroHandler, MaybeTypeOf, Protocol, + TypeInfo, TypeOf, }; mod sealed { @@ -111,6 +112,31 @@ impl FunctionMacroData { } } +/// Runtime data for an attribute macro. +#[derive(Clone)] +pub struct AttributeMacroData { + pub(crate) item: ItemBuf, + pub(crate) handler: Arc, +} + +impl AttributeMacroData { + #[inline] + pub(crate) fn new(name: N, f: F) -> Self + where + F: 'static + + Send + + Sync + + Fn(&mut MacroContext<'_>, &TokenStream, &TokenStream) -> compile::Result, + N: IntoIterator, + N::Item: IntoComponent, + { + Self { + item: ItemBuf::with_item(name), + handler: Arc::new(f), + } + } +} + /// A descriptor for an instance function. #[derive(Debug, Clone)] #[non_exhaustive] @@ -325,6 +351,8 @@ where pub enum MacroMetaKind { #[doc(hidden)] Function(FunctionMacroData), + #[doc(hidden)] + Attribute(AttributeMacroData), } impl MacroMetaKind { @@ -341,6 +369,20 @@ impl MacroMetaKind { { Self::Function(FunctionMacroData::new(name, f)) } + + #[doc(hidden)] + #[inline] + pub fn attribute(name: N, f: F) -> Self + where + F: 'static + + Send + + Sync + + Fn(&mut MacroContext<'_>, &TokenStream, &TokenStream) -> compile::Result, + N: IntoIterator, + N::Item: IntoComponent, + { + Self::Attribute(AttributeMacroData::new(name, f)) + } } /// The data of a [`MacroMeta`]. diff --git a/crates/rune/src/module/module.rs b/crates/rune/src/module/module.rs index 63186ae52..dcd723842 100644 --- a/crates/rune/src/module/module.rs +++ b/crates/rune/src/module/module.rs @@ -12,12 +12,13 @@ use crate::module::function_meta::{ }; use crate::module::{ AssociatedKey, Async, EnumMut, Function, FunctionKind, InstallWith, InstanceFunction, - InternalEnum, InternalEnumMut, ItemMut, ModuleAssociated, ModuleConstant, ModuleFunction, - ModuleMacro, ModuleType, Plain, TypeMut, TypeSpecification, UnitType, VariantMut, + InternalEnum, InternalEnumMut, ItemMut, ModuleAssociated, ModuleAttributeMacro, ModuleConstant, + ModuleFunction, ModuleMacro, ModuleType, Plain, TypeMut, TypeSpecification, UnitType, + VariantMut, }; use crate::runtime::{ - ConstValue, FromValue, GeneratorState, MacroHandler, MaybeTypeOf, Protocol, Stack, ToValue, - TypeCheck, TypeOf, Value, VmResult, + AttributeMacroHandler, ConstValue, FromValue, GeneratorState, MacroHandler, MaybeTypeOf, + Protocol, Stack, ToValue, TypeCheck, TypeOf, Value, VmResult, }; use crate::Hash; @@ -29,6 +30,8 @@ enum Name { Item(Hash), /// A macro. Macro(Hash), + /// An attribute macro. + AttributeMacro(Hash), } /// A [Module] that is a collection of native functions and types. @@ -47,6 +50,8 @@ pub struct Module { pub(crate) functions: Vec, /// MacroHandler handlers. pub(crate) macros: Vec, + /// AttributeMacroHandler handlers. + pub(crate) attribute_macros: Vec, /// Constant values. pub(crate) constants: Vec, /// Associated items. @@ -108,6 +113,7 @@ impl Module { item, functions: Vec::new(), macros: Vec::new(), + attribute_macros: Vec::new(), associated: Vec::new(), types: Vec::new(), types_hash: HashMap::new(), @@ -537,7 +543,7 @@ impl Module { /// /// ``` /// #[rune::macro_] /// fn ident_to_string(ctx: &mut MacroContext<'_>, stream: &TokenStream) -> compile::Result { - /// let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + /// let mut p = Parser::from_token_stream(stream, ctx.input_span()); /// let ident = p.parse_all::()?; /// let ident = ctx.resolve(ident)?.to_owned(); /// let string = ctx.lit(&ident); @@ -552,7 +558,7 @@ impl Module { pub fn macro_meta(&mut self, meta: MacroMeta) -> Result, ContextError> { let meta = meta(); - match meta.kind { + let docs = match meta.kind { MacroMetaKind::Function(data) => { let hash = Hash::type_hash(&data.item); @@ -571,17 +577,37 @@ impl Module { handler: data.handler, docs, }); + &mut self.macros.last_mut().unwrap().docs } - } + MacroMetaKind::Attribute(data) => { + let hash = Hash::type_hash(&data.item); - let m = self.macros.last_mut().unwrap(); - Ok(ItemMut { docs: &mut m.docs }) + if !self.names.insert(Name::AttributeMacro(hash)) { + return Err(ContextError::ConflictingMacroName { + item: data.item, + hash, + }); + } + + let mut docs = Docs::EMPTY; + docs.set_docs(meta.docs); + + self.attribute_macros.push(ModuleAttributeMacro { + item: data.item, + handler: data.handler, + docs, + }); + &mut self.attribute_macros.last_mut().unwrap().docs + } + }; + + Ok(ItemMut { docs }) } /// Register a native macro handler. /// - /// If possible, [`Module::function_meta`] should be used since it includes more - /// useful information about the function. + /// If possible, [`Module::macro_meta`] should be used since it includes more + /// useful information about the macro. /// /// # Examples /// @@ -593,7 +619,7 @@ impl Module { /// use rune::parse::Parser; /// /// fn ident_to_string(ctx: &mut MacroContext<'_>, stream: &TokenStream) -> compile::Result { - /// let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + /// let mut p = Parser::from_token_stream(stream, ctx.input_span()); /// let ident = p.parse_all::()?; /// let ident = ctx.resolve(ident)?.to_owned(); /// let string = ctx.lit(&ident); @@ -632,6 +658,61 @@ impl Module { Ok(ItemMut { docs: &mut m.docs }) } + /// Register a native attribute macro handler. + /// + /// If possible, [`Module::macro_meta`] should be used since it includes more + /// useful information about the function. + /// + /// # Examples + /// + /// ``` + /// use rune::Module; + /// use rune::ast; + /// use rune::compile; + /// use rune::macros::{quote, MacroContext, TokenStream, ToTokens}; + /// use rune::parse::Parser; + /// + /// fn rename_fn(ctx: &mut MacroContext<'_>, input: &TokenStream, item: &TokenStream) -> compile::Result { + /// let mut item = Parser::from_token_stream(item, ctx.macro_span()); + /// let mut fun = item.parse_all::()?; + /// + /// let mut input = Parser::from_token_stream(input, ctx.input_span()); + /// fun.name = input.parse_all::>()?.value; + /// Ok(quote!(#fun).into_token_stream(ctx)) + /// } + /// + /// let mut m = Module::new(); + /// m.attribute_macro(["rename_fn"], rename_fn)?; + /// # Ok::<_, rune::Error>(()) + /// ``` + pub fn attribute_macro(&mut self, name: N, f: M) -> Result, ContextError> + where + M: 'static + + Send + + Sync + + Fn(&mut MacroContext<'_>, &TokenStream, &TokenStream) -> compile::Result, + N: IntoIterator, + N::Item: IntoComponent, + { + let item = ItemBuf::with_item(name); + let hash = Hash::type_hash(&item); + + if !self.names.insert(Name::AttributeMacro(hash)) { + return Err(ContextError::ConflictingMacroName { item, hash }); + } + + let handler: Arc = Arc::new(f); + + self.attribute_macros.push(ModuleAttributeMacro { + item, + handler, + docs: Docs::EMPTY, + }); + + let m = self.attribute_macros.last_mut().unwrap(); + Ok(ItemMut { docs: &mut m.docs }) + } + /// Register a function handler through its meta. /// /// The metadata must be provided by annotating the function with diff --git a/crates/rune/src/modules/core.rs b/crates/rune/src/modules/core.rs index e5ce59efb..330c5a7ba 100644 --- a/crates/rune/src/modules/core.rs +++ b/crates/rune/src/modules/core.rs @@ -103,7 +103,7 @@ pub(crate) fn panic_macro( ctx: &mut MacroContext<'_>, stream: &TokenStream, ) -> compile::Result { - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let args = p.parse_all::()?; let expanded = args.expand(ctx)?; Ok(quote!(::std::panic(#expanded)).into_token_stream(ctx)) diff --git a/crates/rune/src/modules/fmt.rs b/crates/rune/src/modules/fmt.rs index 2d644c041..6fa1404f1 100644 --- a/crates/rune/src/modules/fmt.rs +++ b/crates/rune/src/modules/fmt.rs @@ -40,7 +40,7 @@ pub(crate) fn format( ctx: &mut MacroContext<'_>, stream: &TokenStream, ) -> compile::Result { - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let args = p.parse::()?; p.eof()?; let expanded = args.expand(ctx)?; diff --git a/crates/rune/src/modules/io.rs b/crates/rune/src/modules/io.rs index aab4db8ba..add7c91b6 100644 --- a/crates/rune/src/modules/io.rs +++ b/crates/rune/src/modules/io.rs @@ -126,7 +126,7 @@ pub(crate) fn print_macro( ctx: &mut MacroContext<'_>, stream: &TokenStream, ) -> compile::Result { - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let args = p.parse_all::()?; let expanded = args.expand(ctx)?; Ok(quote!(::std::io::print(#expanded)).into_token_stream(ctx)) @@ -173,7 +173,7 @@ pub(crate) fn println_macro( ctx: &mut MacroContext<'_>, stream: &TokenStream, ) -> compile::Result { - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let args = p.parse_all::()?; let expanded = args.expand(ctx)?; Ok(quote!(::std::io::println(#expanded)).into_token_stream(ctx)) diff --git a/crates/rune/src/modules/macros.rs b/crates/rune/src/modules/macros.rs index 9039aafae..01b963e94 100644 --- a/crates/rune/src/modules/macros.rs +++ b/crates/rune/src/modules/macros.rs @@ -31,7 +31,7 @@ pub(crate) fn line( ) -> compile::Result { use crate as rune; - let mut parser = Parser::from_token_stream(stream, ctx.stream_span()); + let mut parser = Parser::from_token_stream(stream, ctx.input_span()); parser.eof()?; Ok(quote!( @@ -55,7 +55,7 @@ pub(crate) fn file( ) -> compile::Result { use crate as rune; - let mut parser = Parser::from_token_stream(stream, ctx.stream_span()); + let mut parser = Parser::from_token_stream(stream, ctx.input_span()); parser.eof()?; Ok(quote!( diff --git a/crates/rune/src/modules/test.rs b/crates/rune/src/modules/test.rs index ce81c2b24..372b65b46 100644 --- a/crates/rune/src/modules/test.rs +++ b/crates/rune/src/modules/test.rs @@ -63,7 +63,7 @@ pub(crate) fn assert( ) -> compile::Result { use crate as rune; - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let expr = p.parse::()?; let message = if p.parse::>()?.is_some() { @@ -108,7 +108,7 @@ pub(crate) fn assert_eq( ) -> compile::Result { use crate as rune; - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let left = p.parse::()?; p.parse::()?; let right = p.parse::()?; diff --git a/crates/rune/src/runtime.rs b/crates/rune/src/runtime.rs index b0609103c..e4862cb9a 100644 --- a/crates/rune/src/runtime.rs +++ b/crates/rune/src/runtime.rs @@ -92,7 +92,7 @@ pub use rune_core::RawStr; mod runtime_context; pub use self::runtime_context::RuntimeContext; -pub(crate) use self::runtime_context::{FunctionHandler, MacroHandler}; +pub(crate) use self::runtime_context::{AttributeMacroHandler, FunctionHandler, MacroHandler}; mod select; pub(crate) use self::select::Select; diff --git a/crates/rune/src/runtime/runtime_context.rs b/crates/rune/src/runtime/runtime_context.rs index dc2d1a81b..8f755cb40 100644 --- a/crates/rune/src/runtime/runtime_context.rs +++ b/crates/rune/src/runtime/runtime_context.rs @@ -15,6 +15,11 @@ pub(crate) type FunctionHandler = dyn Fn(&mut Stack, usize) -> VmResult<()> + Se pub(crate) type MacroHandler = dyn Fn(&mut MacroContext, &TokenStream) -> compile::Result + Send + Sync; +/// A (type erased) attribute macro handler. +pub(crate) type AttributeMacroHandler = dyn Fn(&mut MacroContext, &TokenStream, &TokenStream) -> compile::Result + + Send + + Sync; + /// Static run context visible to the virtual machine. /// /// This contains: diff --git a/crates/rune/src/testing.rs b/crates/rune/src/testing.rs index 965417dba..3c56c9794 100644 --- a/crates/rune/src/testing.rs +++ b/crates/rune/src/testing.rs @@ -42,7 +42,7 @@ where let ast2 = MacroContext::test(|ctx| { let mut stream = TokenStream::new(); ast.to_tokens(ctx, &mut stream); - let mut parser = Parser::from_token_stream(&stream, ctx.stream_span()); + let mut parser = Parser::from_token_stream(&stream, ctx.input_span()); let ast2 = expect!(parser.parse::(), "Second parse"); expect!(parser.eof(), "Second parse EOF"); ast2 diff --git a/crates/rune/src/tests/custom_macros.rs b/crates/rune/src/tests/custom_macros.rs index 4afe32d35..5bdeea5fc 100644 --- a/crates/rune/src/tests/custom_macros.rs +++ b/crates/rune/src/tests/custom_macros.rs @@ -2,6 +2,7 @@ prelude!(); use std::sync::Arc; +use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use macros::quote; use parse::Parser; @@ -19,7 +20,7 @@ fn test_parse_in_macro() -> Result<()> { })?; m.macro_(["string_as_code_from_arg"], |ctx, stream| { - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let s = p.parse_all::()?; let s = ctx.resolve(s)?.into_owned(); let id = ctx.insert_source("string_as_code_from_arg", &s); @@ -50,3 +51,109 @@ fn test_parse_in_macro() -> Result<()> { assert_eq!(output, (42, 42)); Ok(()) } + +#[test] +fn conflicting_attribute_function() -> Result<()> { + let mut m = Module::default(); + + m.macro_(["conflicting"], move |ctx, _| { + Ok(quote!(21).into_token_stream(ctx)) + })?; + + m.attribute_macro(["conflicting"], |ctx, _, _| { + Ok(quote!( + fn hello() { + 21 + } + ) + .into_token_stream(ctx)) + })?; + + let mut context = Context::with_default_modules()?; + context.install(m)?; + + let mut sources = sources! { + entry => { + pub fn main() { + hello() + conflicting!() + } + + #[conflicting] + fn hi() {} + } + }; + + let unit = prepare(&mut sources).with_context(&context).build()?; + + let mut vm = Vm::new(Arc::new(context.runtime()), Arc::new(unit)); + let output = vm.call(["main"], ())?; + let output: u32 = from_value(output)?; + + assert_eq!(output, 42); + Ok(()) +} + +#[test] +fn attribute_imports_builtin() -> Result<()> { + let mut m = Module::with_crate("abc"); + + m.attribute_macro(["before_use"], |ctx, _, _| { + Ok(quote!( + fn before() { + 21 + } + ) + .into_token_stream(ctx)) + })?; + + m.attribute_macro(["after_use"], |ctx, _, _| { + Ok(quote!( + fn after() { + 21 + } + ) + .into_token_stream(ctx)) + })?; + + let mut context = Context::with_default_modules()?; + context.install(m)?; + + let mut sources = sources! { + entry => { + #[doc = "Doc comment"] + #[test] + pub fn main() { + before() + after() + } + + #[before_use] + fn hi() {} + + use ::abc::{ before_use, after_use }; + + #[after_use] + fn ho() {} + + } + }; + + let diagnostics = &mut Diagnostics::new(); + + let result = rune::prepare(&mut sources) + .with_context(&context) + .with_diagnostics(diagnostics) + .build(); + + if !diagnostics.is_empty() { + diagnostics.emit(&mut StandardStream::stdout(ColorChoice::Auto), &sources)?; + } + + let unit = result?; + + let mut vm = Vm::new(Arc::new(context.runtime()), Arc::new(unit)); + let output = vm.call(["main"], ())?; + let output: u32 = from_value(output)?; + + assert_eq!(output, 42); + Ok(()) +} diff --git a/examples/examples/concat_idents.rs b/examples/examples/concat_idents.rs index 59fec2ebd..f202ddd17 100644 --- a/examples/examples/concat_idents.rs +++ b/examples/examples/concat_idents.rs @@ -10,7 +10,7 @@ use std::sync::Arc; fn concat_idents(ctx: &mut MacroContext<'_>, stream: &TokenStream) -> compile::Result { let mut output = String::new(); - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let ident = p.parse::()?; output.push_str(ctx.resolve(ident)?); diff --git a/examples/examples/parsing_in_macro.rs b/examples/examples/parsing_in_macro.rs index fa9e16eff..deb64d267 100644 --- a/examples/examples/parsing_in_macro.rs +++ b/examples/examples/parsing_in_macro.rs @@ -57,7 +57,7 @@ fn module() -> Result { })?; m.macro_(["string_as_code_from_arg"], |ctx, stream| { - let mut p = Parser::from_token_stream(stream, ctx.stream_span()); + let mut p = Parser::from_token_stream(stream, ctx.input_span()); let s = p.parse_all::()?; let s = ctx.resolve(s)?.into_owned(); let id = ctx.insert_source("string_as_code_from_arg", &s);