summaryrefslogtreecommitdiff
path: root/macros
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-01-07 21:24:36 +0100
committerLaurenz <laurmaedje@gmail.com>2022-01-08 00:20:48 +0100
commite74ae6ce70d4c6ca006613eadf07f920951789e3 (patch)
tree0b9b2ddabf79dad8d55631780ee5d70afe7362d7 /macros
parent0b624390906e911bde325b487b2710b67c8205c8 (diff)
Make all nodes into classes
Diffstat (limited to 'macros')
-rw-r--r--macros/src/lib.rs83
1 files changed, 58 insertions, 25 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 16fb7885..b667d2e2 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -7,48 +7,82 @@ use syn::parse_quote;
use syn::spanned::Spanned;
use syn::{Error, Result};
-/// Generate node properties.
+/// Turn a node into a class.
#[proc_macro_attribute]
-pub fn properties(_: TokenStream, item: TokenStream) -> TokenStream {
+pub fn class(_: TokenStream, item: TokenStream) -> TokenStream {
let impl_block = syn::parse_macro_input!(item as syn::ItemImpl);
expand(impl_block).unwrap_or_else(|err| err.to_compile_error()).into()
}
-/// Expand a property impl block for a node.
+/// Expand an impl block for a node.
fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
// Split the node type into name and generic type arguments.
+ let params = &impl_block.generics.params;
let self_ty = &*impl_block.self_ty;
let (self_name, self_args) = parse_self(self_ty)?;
- // Rewrite the const items from values to keys.
- let mut modules = vec![];
- for item in &mut impl_block.items {
- if let syn::ImplItem::Const(item) = item {
- let module = process_const(
- item,
- &impl_block.generics,
- self_ty,
- &self_name,
- &self_args,
- )?;
- modules.push(module);
+ let module = quote::format_ident!("{}_types", self_name);
+
+ let mut key_modules = vec![];
+ let mut construct = None;
+ let mut set = None;
+
+ for item in std::mem::take(&mut impl_block.items) {
+ match item {
+ syn::ImplItem::Const(mut item) => {
+ key_modules.push(process_const(
+ &mut item, params, self_ty, &self_name, &self_args,
+ )?);
+ impl_block.items.push(syn::ImplItem::Const(item));
+ }
+ syn::ImplItem::Method(method) => {
+ match method.sig.ident.to_string().as_str() {
+ "construct" => construct = Some(method),
+ "set" => set = Some(method),
+ _ => return Err(Error::new(method.span(), "unexpected method")),
+ }
+ }
+ _ => return Err(Error::new(item.span(), "unexpected item")),
}
}
+ let construct =
+ construct.ok_or_else(|| Error::new(impl_block.span(), "missing constructor"))?;
+
+ let set = if impl_block.items.is_empty() {
+ set.unwrap_or_else(|| {
+ parse_quote! {
+ fn set(_: &mut Args, _: &mut StyleMap) -> TypResult<()> {
+ Ok(())
+ }
+ }
+ })
+ } else {
+ set.ok_or_else(|| Error::new(impl_block.span(), "missing set method"))?
+ };
+
// Put everything into a module with a hopefully unique type to isolate
// it from the outside.
- let module = quote::format_ident!("{}_types", self_name);
Ok(quote! {
#[allow(non_snake_case)]
mod #module {
use std::any::TypeId;
use std::marker::PhantomData;
use once_cell::sync::Lazy;
- use crate::eval::{Nonfolding, Property};
+ use crate::eval::{Construct, Nonfolding, Property, Set};
use super::*;
#impl_block
- #(#modules)*
+
+ impl<#params> Construct for #self_ty {
+ #construct
+ }
+
+ impl<#params> Set for #self_ty {
+ #set
+ }
+
+ #(#key_modules)*
}
})
}
@@ -82,7 +116,7 @@ fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> {
/// Process a single const item.
fn process_const(
item: &mut syn::ImplItemConst,
- impl_generics: &syn::Generics,
+ params: &syn::punctuated::Punctuated<syn::GenericParam, syn::Token![,]>,
self_ty: &syn::Type,
self_name: &str,
self_args: &[&syn::Type],
@@ -95,7 +129,6 @@ fn process_const(
let value_ty = &item.ty;
// ... but the real type of the const becomes Key<#key_args>.
- let key_params = &impl_generics.params;
let key_args = quote! { #value_ty #(, #self_args)* };
// The display name, e.g. `TextNode::STRONG`.
@@ -107,7 +140,7 @@ fn process_const(
let mut folder = None;
let mut nonfolding = Some(quote! {
- impl<#key_params> Nonfolding for Key<#key_args> {}
+ impl<#params> Nonfolding for Key<#key_args> {}
});
// Look for a folding function like `#[fold(u64::add)]`.
@@ -132,16 +165,16 @@ fn process_const(
mod #module_name {
use super::*;
- pub struct Key<T, #key_params>(pub PhantomData<(T, #key_args)>);
+ pub struct Key<VALUE, #params>(pub PhantomData<(VALUE, #key_args)>);
- impl<#key_params> Copy for Key<#key_args> {}
- impl<#key_params> Clone for Key<#key_args> {
+ impl<#params> Copy for Key<#key_args> {}
+ impl<#params> Clone for Key<#key_args> {
fn clone(&self) -> Self {
*self
}
}
- impl<#key_params> Property for Key<#key_args> {
+ impl<#params> Property for Key<#key_args> {
type Value = #value_ty;
const NAME: &'static str = #name;