summaryrefslogtreecommitdiff
path: root/macros
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-12-14 10:09:44 +0100
committerLaurenz <laurmaedje@gmail.com>2022-12-14 10:09:44 +0100
commit9ba4d2c134479aad876a0e2ac4cd1622a353109e (patch)
treea94e0e6ae53a1ba440e869fca26cc2ea0b179057 /macros
parent4c73456fc1f5df8ebb3a89d9db657c3c54624d66 (diff)
New macro setup
Diffstat (limited to 'macros')
-rw-r--r--macros/src/capability.rs44
-rw-r--r--macros/src/func.rs82
-rw-r--r--macros/src/lib.rs25
-rw-r--r--macros/src/node.rs40
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;