diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-12-14 10:09:44 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-12-14 10:09:44 +0100 |
| commit | 9ba4d2c134479aad876a0e2ac4cd1622a353109e (patch) | |
| tree | a94e0e6ae53a1ba440e869fca26cc2ea0b179057 /macros/src | |
| parent | 4c73456fc1f5df8ebb3a89d9db657c3c54624d66 (diff) | |
New macro setup
Diffstat (limited to 'macros/src')
| -rw-r--r-- | macros/src/capability.rs | 44 | ||||
| -rw-r--r-- | macros/src/func.rs | 82 | ||||
| -rw-r--r-- | macros/src/lib.rs | 25 | ||||
| -rw-r--r-- | macros/src/node.rs | 40 |
4 files changed, 146 insertions, 45 deletions
diff --git a/macros/src/capability.rs b/macros/src/capability.rs index 7dd4c42a..aa98f584 100644 --- a/macros/src/capability.rs +++ b/macros/src/capability.rs @@ -1,10 +1,48 @@ use super::*; +use syn::parse::Parser; +use syn::punctuated::Punctuated; +use syn::Token; + /// Expand the `#[capability]` macro. -pub fn expand(body: syn::ItemTrait) -> Result<TokenStream> { - let ident = &body.ident; +pub fn capability(item: syn::ItemTrait) -> Result<TokenStream> { + let ident = &item.ident; Ok(quote! { - #body + #item impl ::typst::model::Capability for dyn #ident {} }) } + +/// Expand the `#[capable(..)]` macro. +pub fn capable(attr: TokenStream, item: syn::Item) -> Result<TokenStream> { + let (ident, generics) = match &item { + syn::Item::Struct(s) => (&s.ident, &s.generics), + syn::Item::Enum(s) => (&s.ident, &s.generics), + _ => bail!(item, "only structs and enums are supported"), + }; + + let (params, args, clause) = generics.split_for_impl(); + let checks = Punctuated::<Ident, Token![,]>::parse_terminated + .parse2(attr)? + .into_iter() + .map(|capability| { + quote! { + if id == ::std::any::TypeId::of::<dyn #capability>() { + return Some(unsafe { + ::typst::util::fat::vtable(self as &dyn #capability) + }); + } + } + }); + + Ok(quote! { + #item + + unsafe impl #params ::typst::model::Capable for #ident #args #clause { + fn vtable(&self, id: ::std::any::TypeId) -> ::std::option::Option<*const ()> { + #(#checks)* + None + } + } + }) +} diff --git a/macros/src/func.rs b/macros/src/func.rs new file mode 100644 index 00000000..af558f96 --- /dev/null +++ b/macros/src/func.rs @@ -0,0 +1,82 @@ +use super::*; + +/// Expand the `#[func]` macro. +pub fn func(item: syn::Item) -> Result<TokenStream> { + let doc = documentation(&item)?; + + if let syn::Item::Fn(item) = &item { + let vis = &item.vis; + let ident = &item.sig.ident; + let s = ident.to_string(); + let mut chars = s.trim_end_matches("_").chars(); + let ty = quote::format_ident!( + "{}{}Func", + chars.next().unwrap().to_ascii_uppercase(), + chars.as_str() + ); + + let full = if item.sig.inputs.len() == 1 { + quote! { |_, args| #ident(args) } + } else { + quote! { #ident } + }; + + Ok(quote! { + #item + + #[doc(hidden)] + #vis enum #ty {} + + impl::typst::model::FuncType for #ty { + fn create_func(name: &'static str) -> ::typst::model::Func { + ::typst::model::Func::from_fn(name, #full, #doc) + } + } + }) + } else { + let (ident, generics) = match &item { + syn::Item::Struct(s) => (&s.ident, &s.generics), + syn::Item::Enum(s) => (&s.ident, &s.generics), + _ => bail!(item, "only structs, enums, and functions are supported"), + }; + + let (params, args, clause) = generics.split_for_impl(); + + Ok(quote! { + #item + + impl #params ::typst::model::FuncType for #ident #args #clause { + fn create_func(name: &'static str) -> ::typst::model::Func { + ::typst::model::Func::from_node::<Self>(name, #doc) + } + } + }) + } +} + +/// Extract the item's documentation. +fn documentation(item: &syn::Item) -> Result<String> { + let mut doc = String::new(); + + // Extract attributes. + let attrs = match item { + syn::Item::Struct(item) => &item.attrs, + syn::Item::Enum(item) => &item.attrs, + syn::Item::Fn(item) => &item.attrs, + _ => return Ok(doc), + }; + + // Parse doc comments. + for attr in attrs { + if let syn::Meta::NameValue(meta) = attr.parse_meta()? { + if meta.path.is_ident("doc") { + if let syn::Lit::Str(string) = &meta.lit { + doc.push_str(&string.value()); + doc.push('\n'); + } + } + } + } + + Ok(doc.trim().into()) +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 699deee9..7f6a4a6c 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -13,6 +13,7 @@ macro_rules! bail { } mod capability; +mod func; mod node; use proc_macro::TokenStream as BoundaryStream; @@ -21,20 +22,34 @@ use quote::{quote, quote_spanned}; use syn::parse_quote; use syn::{Error, Ident, Result}; +/// Implement `FuncType` for a type or function. +#[proc_macro_attribute] +pub fn func(_: BoundaryStream, item: BoundaryStream) -> BoundaryStream { + let item = syn::parse_macro_input!(item as syn::Item); + func::func(item).unwrap_or_else(|err| err.to_compile_error()).into() +} + /// Implement `Node` for a struct. #[proc_macro_attribute] -pub fn node(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream { +pub fn node(_: BoundaryStream, item: BoundaryStream) -> BoundaryStream { let item = syn::parse_macro_input!(item as syn::ItemImpl); - node::expand(stream.into(), item) - .unwrap_or_else(|err| err.to_compile_error()) - .into() + node::node(item).unwrap_or_else(|err| err.to_compile_error()).into() } /// Implement `Capability` for a trait. #[proc_macro_attribute] pub fn capability(_: BoundaryStream, item: BoundaryStream) -> BoundaryStream { let item = syn::parse_macro_input!(item as syn::ItemTrait); - capability::expand(item) + capability::capability(item) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + +/// Implement `Capable` for a type. +#[proc_macro_attribute] +pub fn capable(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream { + let item = syn::parse_macro_input!(item as syn::Item); + capability::capable(stream.into(), item) .unwrap_or_else(|err| err.to_compile_error()) .into() } diff --git a/macros/src/node.rs b/macros/src/node.rs index 4add9bc3..192ca667 100644 --- a/macros/src/node.rs +++ b/macros/src/node.rs @@ -1,4 +1,3 @@ -use syn::parse::Parser; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::Token; @@ -6,8 +5,8 @@ use syn::Token; use super::*; /// Expand the `#[node]` macro. -pub fn expand(attr: TokenStream, body: syn::ItemImpl) -> Result<TokenStream> { - let node = prepare(attr, body)?; +pub fn node(body: syn::ItemImpl) -> Result<TokenStream> { + let node = prepare(body)?; create(&node) } @@ -18,7 +17,6 @@ struct Node { self_ty: syn::Type, self_name: String, self_args: Punctuated<syn::GenericArgument, Token![,]>, - capabilities: Vec<syn::Ident>, properties: Vec<Property>, construct: Option<syn::ImplItemMethod>, set: Option<syn::ImplItemMethod>, @@ -47,7 +45,7 @@ enum Shorthand { } /// Preprocess the impl block of a node. -fn prepare(attr: TokenStream, body: syn::ItemImpl) -> Result<Node> { +fn prepare(body: syn::ItemImpl) -> Result<Node> { // Extract the generic type arguments. let params = body.generics.params.clone(); @@ -66,12 +64,6 @@ fn prepare(attr: TokenStream, body: syn::ItemImpl) -> Result<Node> { _ => Punctuated::new(), }; - // Parse the capabilities. - let capabilities: Vec<_> = Punctuated::<Ident, Token![,]>::parse_terminated - .parse2(attr)? - .into_iter() - .collect(); - let mut properties = vec![]; let mut construct = None; let mut set = None; @@ -101,7 +93,6 @@ fn prepare(attr: TokenStream, body: syn::ItemImpl) -> Result<Node> { self_ty, self_name, self_args, - capabilities, properties, construct, set, @@ -215,7 +206,6 @@ fn create(node: &Node) -> Result<TokenStream> { let construct_func = create_node_construct_func(node); let set_func = create_node_set_func(node); let field_method = create_node_field_method(node); - let vtable_method = create_node_vtable_method(node); let node_impl = quote! { impl<#params> ::typst::model::Node for #self_ty { @@ -225,10 +215,6 @@ fn create(node: &Node) -> Result<TokenStream> { #set_func #field_method } - - unsafe impl<#params> ::typst::model::Capable for #self_ty { - #vtable_method - } }; let mut modules: Vec<syn::ItemMod> = vec![]; @@ -359,26 +345,6 @@ fn create_node_field_method(node: &Node) -> syn::ImplItemMethod { }) } -/// Create the node's capability accessor method. -fn create_node_vtable_method(node: &Node) -> syn::ImplItemMethod { - let checks = node.capabilities.iter().map(|capability| { - quote! { - if id == ::std::any::TypeId::of::<dyn #capability>() { - return Some(unsafe { - ::typst::util::fat::vtable(self as &dyn #capability) - }); - } - } - }); - - parse_quote! { - fn vtable(&self, id: ::std::any::TypeId) -> ::std::option::Option<*const ()> { - #(#checks)* - None - } - } -} - /// Process a single const item. fn create_property_module(node: &Node, property: &Property) -> (syn::Type, syn::ItemMod) { let params = &node.params; |
